1 use winnow::combinator::alt;
2 use winnow::combinator::fail;
3 use winnow::combinator::peek;
4 use winnow::token::any;
5 
6 use crate::parser::array::array;
7 use crate::parser::datetime::date_time;
8 use crate::parser::inline_table::inline_table;
9 use crate::parser::numbers::{float, integer};
10 use crate::parser::prelude::*;
11 use crate::parser::strings::string;
12 use crate::repr::{Formatted, Repr};
13 use crate::value as v;
14 use crate::RawString;
15 use crate::Value;
16 
17 // val = string / boolean / array / inline-table / date-time / float / integer
value<'i>(check: RecursionCheck) -> impl Parser<Input<'i>, v::Value, ContextError>18 pub(crate) fn value<'i>(check: RecursionCheck) -> impl Parser<Input<'i>, v::Value, ContextError> {
19     move |input: &mut Input<'i>| {
20         dispatch!{peek(any);
21             crate::parser::strings::QUOTATION_MARK |
22             crate::parser::strings::APOSTROPHE => string.map(|s| {
23                 v::Value::String(Formatted::new(
24                     s.into_owned()
25                 ))
26             }),
27             crate::parser::array::ARRAY_OPEN => array(check).map(v::Value::Array),
28             crate::parser::inline_table::INLINE_TABLE_OPEN => inline_table(check).map(v::Value::InlineTable),
29             // Date/number starts
30             b'+' | b'-' | b'0'..=b'9' => {
31                 // Uncommon enough not to be worth optimizing at this time
32                 alt((
33                     date_time
34                         .map(v::Value::from),
35                     float
36                         .map(v::Value::from),
37                     integer
38                         .map(v::Value::from),
39                 ))
40             },
41             // Report as if they were numbers because its most likely a typo
42             b'_' => {
43                     integer
44                         .map(v::Value::from)
45                 .context(StrContext::Expected(StrContextValue::Description("leading digit")))
46             },
47             // Report as if they were numbers because its most likely a typo
48             b'.' =>  {
49                     float
50                         .map(v::Value::from)
51                 .context(StrContext::Expected(StrContextValue::Description("leading digit")))
52             },
53             b't' => {
54                 crate::parser::numbers::true_.map(v::Value::from)
55                     .context(StrContext::Label("string"))
56                     .context(StrContext::Expected(StrContextValue::CharLiteral('"')))
57                     .context(StrContext::Expected(StrContextValue::CharLiteral('\'')))
58             },
59             b'f' => {
60                 crate::parser::numbers::false_.map(v::Value::from)
61                     .context(StrContext::Label("string"))
62                     .context(StrContext::Expected(StrContextValue::CharLiteral('"')))
63                     .context(StrContext::Expected(StrContextValue::CharLiteral('\'')))
64             },
65             b'i' => {
66                 crate::parser::numbers::inf.map(v::Value::from)
67                     .context(StrContext::Label("string"))
68                     .context(StrContext::Expected(StrContextValue::CharLiteral('"')))
69                     .context(StrContext::Expected(StrContextValue::CharLiteral('\'')))
70             },
71             b'n' => {
72                 crate::parser::numbers::nan.map(v::Value::from)
73                     .context(StrContext::Label("string"))
74                     .context(StrContext::Expected(StrContextValue::CharLiteral('"')))
75                     .context(StrContext::Expected(StrContextValue::CharLiteral('\'')))
76             },
77             _ => {
78                 fail
79                     .context(StrContext::Label("string"))
80                     .context(StrContext::Expected(StrContextValue::CharLiteral('"')))
81                     .context(StrContext::Expected(StrContextValue::CharLiteral('\'')))
82             },
83     }
84         .with_span()
85         .try_map(|(value, span)| apply_raw(value, span))
86         .parse_next(input)
87     }
88 }
89 
apply_raw(mut val: Value, span: std::ops::Range<usize>) -> Result<Value, std::str::Utf8Error>90 fn apply_raw(mut val: Value, span: std::ops::Range<usize>) -> Result<Value, std::str::Utf8Error> {
91     match val {
92         Value::String(ref mut f) => {
93             let raw = RawString::with_span(span);
94             f.set_repr_unchecked(Repr::new_unchecked(raw));
95         }
96         Value::Integer(ref mut f) => {
97             let raw = RawString::with_span(span);
98             f.set_repr_unchecked(Repr::new_unchecked(raw));
99         }
100         Value::Float(ref mut f) => {
101             let raw = RawString::with_span(span);
102             f.set_repr_unchecked(Repr::new_unchecked(raw));
103         }
104         Value::Boolean(ref mut f) => {
105             let raw = RawString::with_span(span);
106             f.set_repr_unchecked(Repr::new_unchecked(raw));
107         }
108         Value::Datetime(ref mut f) => {
109             let raw = RawString::with_span(span);
110             f.set_repr_unchecked(Repr::new_unchecked(raw));
111         }
112         Value::Array(ref mut arr) => {
113             arr.span = Some(span);
114         }
115         Value::InlineTable(ref mut table) => {
116             table.span = Some(span);
117         }
118     };
119     val.decorate("", "");
120     Ok(val)
121 }
122 
123 #[cfg(test)]
124 #[cfg(feature = "parse")]
125 #[cfg(feature = "display")]
126 mod test {
127     use super::*;
128 
129     #[test]
values()130     fn values() {
131         let inputs = [
132             "1979-05-27T00:32:00.999999",
133             "-239",
134             "1e200",
135             "9_224_617.445_991_228_313",
136             r"'''I [dw]on't need \d{2} apples'''",
137             r#"'''
138 The first newline is
139 trimmed in raw strings.
140    All other whitespace
141    is preserved.
142 '''"#,
143             r#""Jos\u00E9\n""#,
144             r#""\\\"\b/\f\n\r\t\u00E9\U000A0000""#,
145             r#"{ hello = "world", a = 1}"#,
146             r#"[ { x = 1, a = "2" }, {a = "a",b = "b",     c =    "c"} ]"#,
147         ];
148         for input in inputs {
149             dbg!(input);
150             let mut parsed = value(Default::default()).parse(new_input(input));
151             if let Ok(parsed) = &mut parsed {
152                 parsed.despan(input);
153             }
154             assert_eq!(parsed.map(|a| a.to_string()), Ok(input.to_owned()));
155         }
156     }
157 }
158