xsd_types/lexical/
g_year.rs

1use static_regular_grammar::RegularGrammar;
2
3use crate::{lexical::parse_timezone, utils::byte_index_of};
4
5use super::{Lexical, LexicalFormOf};
6
7/// GYear.
8///
9/// ```abnf
10/// g-year = year [timezone]
11///
12/// year = [ "-" ] year-number
13///
14/// year-number = *3DIGIT NZDIGIT
15///             / *2DIGIT NZDIGIT DIGIT
16///             / *1DIGIT NZDIGIT 2DIGIT
17///             / NZDIGIT 3*DIGIT
18///
19/// minute = ("0" / "1" / "2" / "3" / "4" / "5") DIGIT
20///
21/// timezone = ("+" / "-") ((("0" DIGIT / "1" ("0" / "1" / "2" / "3")) ":" minute) / "14:00")
22///          / %s"Z"
23///
24/// NZDIGIT = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9"
25/// ```
26#[derive(RegularGrammar, PartialEq, Eq, PartialOrd, Ord, Hash)]
27#[grammar(sized(GYearBuf, derive(PartialEq, Eq, PartialOrd, Ord, Hash)))]
28pub struct GYear(str);
29
30impl GYear {
31	pub fn parts(&self) -> Parts {
32		let year_end =
33			byte_index_of(self.0.as_bytes(), 4, [b'+', b'-', b'Z']).unwrap_or(self.0.len());
34
35		Parts {
36			year: &self.0[..year_end],
37			timezone: if self.0.len() > year_end {
38				Some(&self.0[year_end..])
39			} else {
40				None
41			},
42		}
43	}
44}
45
46impl Lexical for GYear {
47	type Error = InvalidGYear<String>;
48
49	fn parse(value: &str) -> Result<&Self, Self::Error> {
50		Self::new(value).map_err(|_| InvalidGYear(value.to_owned()))
51	}
52}
53
54impl LexicalFormOf<crate::GYear> for GYear {
55	type ValueError = std::convert::Infallible;
56
57	fn try_as_value(&self) -> Result<crate::GYear, Self::ValueError> {
58		Ok(self.parts().to_g_year_month())
59	}
60}
61
62#[derive(Debug, PartialEq, Eq)]
63pub struct Parts<'a> {
64	pub year: &'a str,
65	pub timezone: Option<&'a str>,
66}
67
68impl<'a> Parts<'a> {
69	pub fn new(year: &'a str, timezone: Option<&'a str>) -> Self {
70		Self { year, timezone }
71	}
72
73	fn to_g_year_month(&self) -> crate::GYear {
74		crate::GYear::new(
75			self.year.parse().unwrap(),
76			self.timezone.map(parse_timezone),
77		)
78	}
79}
80
81#[cfg(test)]
82mod tests {
83	use super::*;
84
85	#[test]
86	fn parsing() {
87		let vectors = [
88			("2014", Parts::new("2014", None)),
89			("-0001Z", Parts::new("-0001", Some("Z"))),
90			("10000+05:00", Parts::new("10000", Some("+05:00"))),
91		];
92
93		for (input, parts) in vectors {
94			let lexical_repr = GYear::new(input).unwrap();
95			assert_eq!(lexical_repr.parts(), parts);
96
97			let value = lexical_repr.try_as_value().unwrap();
98			assert_eq!(value.to_string().as_str(), input)
99		}
100	}
101}