toml_edit/parser/
value.rs

1use winnow::combinator::alt;
2use winnow::combinator::fail;
3use winnow::combinator::peek;
4use winnow::token::any;
5
6use crate::parser::array::array;
7use crate::parser::datetime::date_time;
8use crate::parser::inline_table::inline_table;
9use crate::parser::numbers::{float, integer};
10use crate::parser::prelude::*;
11use crate::parser::strings::string;
12use crate::repr::{Formatted, Repr};
13use crate::value as v;
14use crate::RawString;
15use crate::Value;
16
17// val = string / boolean / array / inline-table / date-time / float / integer
18pub(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
90fn 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)]
124mod test {
125    use super::*;
126
127    #[test]
128    fn values() {
129        let inputs = [
130            "1979-05-27T00:32:00.999999",
131            "-239",
132            "1e200",
133            "9_224_617.445_991_228_313",
134            r#"'''I [dw]on't need \d{2} apples'''"#,
135            r#"'''
136The first newline is
137trimmed in raw strings.
138   All other whitespace
139   is preserved.
140'''"#,
141            r#""Jos\u00E9\n""#,
142            r#""\\\"\b/\f\n\r\t\u00E9\U000A0000""#,
143            r#"{ hello = "world", a = 1}"#,
144            r#"[ { x = 1, a = "2" }, {a = "a",b = "b",     c =    "c"} ]"#,
145        ];
146        for input in inputs {
147            dbg!(input);
148            let mut parsed = value(Default::default()).parse(new_input(input));
149            if let Ok(parsed) = &mut parsed {
150                parsed.despan(input);
151            }
152            assert_eq!(parsed.map(|a| a.to_string()), Ok(input.to_owned()));
153        }
154    }
155}