summaryrefslogtreecommitdiffstats
path: root/vendor/time-macros/src/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/time-macros/src/helpers')
-rw-r--r--vendor/time-macros/src/helpers/mod.rs129
-rw-r--r--vendor/time-macros/src/helpers/string.rs188
2 files changed, 317 insertions, 0 deletions
diff --git a/vendor/time-macros/src/helpers/mod.rs b/vendor/time-macros/src/helpers/mod.rs
new file mode 100644
index 000000000..cbf3ba3ed
--- /dev/null
+++ b/vendor/time-macros/src/helpers/mod.rs
@@ -0,0 +1,129 @@
+#[cfg(any(feature = "formatting", feature = "parsing"))]
+mod string;
+
+use std::iter::Peekable;
+use std::str::FromStr;
+
+#[cfg(any(feature = "formatting", feature = "parsing"))]
+use proc_macro::TokenStream;
+use proc_macro::{token_stream, Span, TokenTree};
+use time_core::util::{days_in_year, is_leap_year};
+
+use crate::Error;
+
+#[cfg(any(feature = "formatting", feature = "parsing"))]
+pub(crate) fn get_string_literal(tokens: TokenStream) -> Result<(Span, Vec<u8>), Error> {
+ let mut tokens = tokens.into_iter();
+
+ match (tokens.next(), tokens.next()) {
+ (Some(TokenTree::Literal(literal)), None) => string::parse(&literal),
+ (Some(tree), None) => Err(Error::ExpectedString {
+ span_start: Some(tree.span()),
+ span_end: Some(tree.span()),
+ }),
+ (_, Some(tree)) => Err(Error::UnexpectedToken { tree }),
+ (None, None) => Err(Error::ExpectedString {
+ span_start: None,
+ span_end: None,
+ }),
+ }
+}
+
+pub(crate) fn consume_number<T: FromStr>(
+ component_name: &'static str,
+ chars: &mut Peekable<token_stream::IntoIter>,
+) -> Result<(Span, T), Error> {
+ let (span, digits) = match chars.next() {
+ Some(TokenTree::Literal(literal)) => (literal.span(), literal.to_string()),
+ Some(tree) => return Err(Error::UnexpectedToken { tree }),
+ None => return Err(Error::UnexpectedEndOfInput),
+ };
+
+ if let Ok(value) = digits.replace('_', "").parse() {
+ Ok((span, value))
+ } else {
+ Err(Error::InvalidComponent {
+ name: component_name,
+ value: digits,
+ span_start: Some(span),
+ span_end: Some(span),
+ })
+ }
+}
+
+pub(crate) fn consume_any_ident(
+ idents: &[&str],
+ chars: &mut Peekable<token_stream::IntoIter>,
+) -> Result<Span, Error> {
+ match chars.peek() {
+ Some(TokenTree::Ident(char)) if idents.contains(&char.to_string().as_str()) => {
+ let ret = Ok(char.span());
+ drop(chars.next());
+ ret
+ }
+ Some(tree) => Err(Error::UnexpectedToken { tree: tree.clone() }),
+ None => Err(Error::UnexpectedEndOfInput),
+ }
+}
+
+pub(crate) fn consume_punct(
+ c: char,
+ chars: &mut Peekable<token_stream::IntoIter>,
+) -> Result<Span, Error> {
+ match chars.peek() {
+ Some(TokenTree::Punct(punct)) if *punct == c => {
+ let ret = Ok(punct.span());
+ drop(chars.next());
+ ret
+ }
+ Some(tree) => Err(Error::UnexpectedToken { tree: tree.clone() }),
+ None => Err(Error::UnexpectedEndOfInput),
+ }
+}
+
+fn jan_weekday(year: i32, ordinal: i32) -> u8 {
+ macro_rules! div_floor {
+ ($a:expr, $b:expr) => {{
+ let (_quotient, _remainder) = ($a / $b, $a % $b);
+ if (_remainder > 0 && $b < 0) || (_remainder < 0 && $b > 0) {
+ _quotient - 1
+ } else {
+ _quotient
+ }
+ }};
+ }
+
+ let adj_year = year - 1;
+ ((ordinal + adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100)
+ + div_floor!(adj_year, 400)
+ + 6)
+ .rem_euclid(7)) as _
+}
+
+pub(crate) fn days_in_year_month(year: i32, month: u8) -> u8 {
+ [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month as usize - 1]
+ + (month == 2 && is_leap_year(year)) as u8
+}
+
+pub(crate) fn ywd_to_yo(year: i32, week: u8, iso_weekday_number: u8) -> (i32, u16) {
+ let (ordinal, overflow) = (u16::from(week) * 7 + u16::from(iso_weekday_number))
+ .overflowing_sub(u16::from(jan_weekday(year, 4)) + 4);
+
+ if overflow || ordinal == 0 {
+ return (year - 1, (ordinal.wrapping_add(days_in_year(year - 1))));
+ }
+
+ let days_in_cur_year = days_in_year(year);
+ if ordinal > days_in_cur_year {
+ (year + 1, ordinal - days_in_cur_year)
+ } else {
+ (year, ordinal)
+ }
+}
+
+pub(crate) fn ymd_to_yo(year: i32, month: u8, day: u8) -> (i32, u16) {
+ let ordinal = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334][month as usize - 1]
+ + (month > 2 && is_leap_year(year)) as u16;
+
+ (year, ordinal + u16::from(day))
+}
diff --git a/vendor/time-macros/src/helpers/string.rs b/vendor/time-macros/src/helpers/string.rs
new file mode 100644
index 000000000..fa3780f5e
--- /dev/null
+++ b/vendor/time-macros/src/helpers/string.rs
@@ -0,0 +1,188 @@
+use std::ops::{Index, RangeFrom};
+
+use proc_macro::Span;
+
+use crate::Error;
+
+pub(crate) fn parse(token: &proc_macro::Literal) -> Result<(Span, Vec<u8>), Error> {
+ let span = token.span();
+ let repr = token.to_string();
+
+ match repr.as_bytes() {
+ [b'"', ..] => Ok((span, parse_lit_str_cooked(&repr[1..]))),
+ [b'b', b'"', rest @ ..] => Ok((span, parse_lit_byte_str_cooked(rest))),
+ [b'r', rest @ ..] | [b'b', b'r', rest @ ..] => Ok((span, parse_lit_str_raw(rest))),
+ _ => Err(Error::ExpectedString {
+ span_start: Some(span),
+ span_end: Some(span),
+ }),
+ }
+}
+
+fn byte(s: impl AsRef<[u8]>, idx: usize) -> u8 {
+ s.as_ref().get(idx).copied().unwrap_or_default()
+}
+
+fn parse_lit_str_cooked(mut s: &str) -> Vec<u8> {
+ let mut content = String::new();
+ 'outer: loop {
+ let ch = match byte(s, 0) {
+ b'"' => break,
+ b'\\' => {
+ let b = byte(s, 1);
+ s = &s[2..];
+ match b {
+ b'x' => {
+ let (byte, rest) = backslash_x(s);
+ s = rest;
+ char::from_u32(u32::from(byte)).expect("byte was just validated")
+ }
+ b'u' => {
+ let (chr, rest) = backslash_u(s);
+ s = rest;
+ chr
+ }
+ b'n' => '\n',
+ b'r' => '\r',
+ b't' => '\t',
+ b'\\' => '\\',
+ b'0' => '\0',
+ b'\'' => '\'',
+ b'"' => '"',
+ b'\r' | b'\n' => loop {
+ let ch = s.chars().next().unwrap_or_default();
+ if ch.is_whitespace() {
+ s = &s[ch.len_utf8()..];
+ } else {
+ continue 'outer;
+ }
+ },
+ _ => unreachable!("invalid escape"),
+ }
+ }
+ b'\r' => {
+ // bare CR not permitted
+ s = &s[2..];
+ '\n'
+ }
+ _ => {
+ let ch = s.chars().next().unwrap_or_default();
+ s = &s[ch.len_utf8()..];
+ ch
+ }
+ };
+ content.push(ch);
+ }
+
+ content.into_bytes()
+}
+
+fn parse_lit_str_raw(s: &[u8]) -> Vec<u8> {
+ let mut pounds = 0;
+ while byte(s, pounds) == b'#' {
+ pounds += 1;
+ }
+ let close = s
+ .iter()
+ .rposition(|&b| b == b'"')
+ .expect("had a string without trailing \"");
+
+ s[pounds + 1..close].to_owned()
+}
+
+fn parse_lit_byte_str_cooked(mut v: &[u8]) -> Vec<u8> {
+ let mut out = Vec::new();
+ 'outer: loop {
+ let byte = match byte(v, 0) {
+ b'"' => break,
+ b'\\' => {
+ let b = byte(v, 1);
+ v = &v[2..];
+ match b {
+ b'x' => {
+ let (byte, rest) = backslash_x(v);
+ v = rest;
+ byte
+ }
+ b'n' => b'\n',
+ b'r' => b'\r',
+ b't' => b'\t',
+ b'\\' => b'\\',
+ b'0' => b'\0',
+ b'\'' => b'\'',
+ b'"' => b'"',
+ b'\r' | b'\n' => loop {
+ let byte = byte(v, 0);
+ let ch = char::from_u32(u32::from(byte)).expect("invalid byte");
+ if ch.is_whitespace() {
+ v = &v[1..];
+ } else {
+ continue 'outer;
+ }
+ },
+ _ => unreachable!("invalid escape"),
+ }
+ }
+ b'\r' => {
+ // bare CR not permitted
+ v = &v[2..];
+ b'\n'
+ }
+ b => {
+ v = &v[1..];
+ b
+ }
+ };
+ out.push(byte);
+ }
+
+ out
+}
+
+fn backslash_x<S>(s: &S) -> (u8, &S)
+where
+ S: Index<RangeFrom<usize>, Output = S> + AsRef<[u8]> + ?Sized,
+{
+ let mut ch = 0;
+ let b0 = byte(s, 0);
+ let b1 = byte(s, 1);
+ ch += 0x10 * (b0 - b'0');
+ ch += match b1 {
+ b'0'..=b'9' => b1 - b'0',
+ b'a'..=b'f' => 10 + (b1 - b'a'),
+ b'A'..=b'F' => 10 + (b1 - b'A'),
+ _ => unreachable!("invalid hex escape"),
+ };
+ (ch, &s[2..])
+}
+
+fn backslash_u(mut s: &str) -> (char, &str) {
+ s = &s[1..];
+
+ let mut ch = 0;
+ let mut digits = 0;
+ loop {
+ let b = byte(s, 0);
+ let digit = match b {
+ b'0'..=b'9' => b - b'0',
+ b'a'..=b'f' => 10 + b - b'a',
+ b'A'..=b'F' => 10 + b - b'A',
+ b'_' if digits > 0 => {
+ s = &s[1..];
+ continue;
+ }
+ b'}' if digits != 0 => break,
+ _ => unreachable!("invalid unicode escape"),
+ };
+ ch *= 0x10;
+ ch += u32::from(digit);
+ digits += 1;
+ s = &s[1..];
+ }
+ s = &s[1..];
+
+ (
+ char::from_u32(ch).expect("invalid unicode escape passed by compiler"),
+ s,
+ )
+}