1use crate::item::Node;
10use crate::parser::combinators::alt::alt4;
11use crate::parser::combinators::many::many0;
12use crate::parser::combinators::map::map;
13use crate::parser::combinators::opt::opt;
14use crate::parser::combinators::support::none_of;
15use crate::parser::combinators::tag::anychar;
16use crate::parser::combinators::tuple::{tuple2, tuple6};
17use crate::parser::{ParseError, ParseInput, ParserState, StaticState, StaticStateBuilder};
18use crate::xdmerror::*;
19use qualname::{NamespacePrefix, NamespaceUri};
20
21#[allow(dead_code)]
24fn picture<'a, N: Node, L>()
25-> impl Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, String), ParseError>
26where
27 L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError>,
28{
29 map(
30 many0(alt4(open_escape(), close_escape(), literal(), marker())),
31 |v| v.iter().cloned().collect::<String>(),
32 )
33}
34
35#[allow(dead_code)]
36fn literal<'a, N: Node, L>()
37-> impl Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, String), ParseError>
38where
39 L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError>,
40{
41 map(none_of("[]"), String::from)
42}
43
44#[allow(dead_code)]
45fn marker<'a, N: Node, L>()
46-> impl Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, String), ParseError>
47where
48 L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError>,
49{
50 map(
51 tuple6(
52 anychar('['),
53 none_of("]"),
54 opt(none_of(",]")),
55 opt(none_of(",]")),
56 opt(tuple2(anychar(','), none_of("]"))),
57 anychar(']'),
58 ),
59 |(_, c, _p1, _p2, _w, _)| {
60 match c {
61 "Y" => String::from("%Y"),
62 "M" => String::from("%m"),
63 "D" => String::from("%d"),
64 "d" => String::from("%j"),
65 "F" => String::from("%A"),
66 "W" => String::from("%U"),
67 "w" => String::from(""), "H" => String::from("%H"),
69 "h" => String::from("%I"),
70 "P" => String::from("%P"),
71 "m" => String::from("%M"),
72 "s" => String::from("%S"),
73 "f" => String::from("%f"),
74 "Z" => String::from("%Z"),
75 "z" => String::from("%:z"), "C" => String::from(""), "E" => String::from(""), _ => String::from(""), }
80 },
81 )
82}
83
84#[allow(dead_code)]
85fn open_escape<'a, N: Node, L>()
86-> impl Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, String), ParseError>
87where
88 L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError>,
89{
90 map(tuple2(anychar('['), anychar('[')), |_| String::from("["))
91}
92#[allow(dead_code)]
93fn close_escape<'a, N: Node, L>()
94-> impl Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, String), ParseError>
95where
96 L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError>,
97{
98 map(tuple2(anychar(']'), anychar(']')), |_| String::from("]"))
99}
100
101pub fn parse<N: Node>(e: &str) -> Result<String, Error> {
102 let state: ParserState<N> = ParserState::new();
103 let mut static_state = StaticStateBuilder::new()
104 .namespace(|_| {
105 NamespaceUri::try_from("urn:xrust").map_err(|_| ParseError::MissingNameSpace)
106 })
107 .build();
108 match picture()((e, state), &mut static_state) {
109 Ok(((rem, _), value)) => {
110 if rem.is_empty() {
111 Ok(value)
112 } else {
113 Err(Error::new(
114 ErrorKind::Unknown,
115 format!("extra characters after expression: \"{}\"", rem),
116 ))
117 }
118 }
119 Err(_) => Err(Error::new(
120 ErrorKind::ParseError,
121 String::from("unable to parse picture"),
122 )),
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129 use crate::trees::nullo::Nullo;
130
131 #[test]
132 fn picture_empty() {
133 let pic = parse::<Nullo>("").expect("failed to parse picture \"\"");
134 assert_eq!(pic, "");
135 }
136
137 #[test]
138 fn picture_date() {
139 let pic = parse::<Nullo>("[D] [M] [Y]").expect("failed to parse picture \"[D] [M] [Y]\"");
140 assert_eq!(pic, "%d %m %Y");
141 }
142
143 #[test]
144 fn picture_time() {
145 let pic = parse::<Nullo>("Hr [h][P] Mins [m] secs [s],[f]")
146 .expect("failed to parse picture \"Hr [h][P] Mins [m] secs [s],[f]\"");
147 assert_eq!(pic, "Hr %I%P Mins %M secs %S,%f");
148 }
149
150 #[test]
151 fn picture_datetime() {
152 let pic = parse::<Nullo>("[D]/[M]/[Y] [H]:[m]:[s]")
153 .expect("failed to parse picture \"[D]/[M]/[Y] [H]:[m]:[s]\"");
154 assert_eq!(pic, "%d/%m/%Y %H:%M:%S");
155 }
156
157 #[test]
158 fn picture_escapes() {
159 let pic = parse::<Nullo>("[[[D]/[M]/[Y]]] [[[H]:[m]:[s]]]")
160 .expect("failed to parse picture \"[D]/[M]/[Y] [H]:[m]:[s]\"");
161 assert_eq!(pic, "[%d/%m/%Y] [%H:%M:%S]");
162 }
163}