toml_edit/parser/
value.rs

1use nom8::branch::alt;
2use nom8::bytes::any;
3use nom8::combinator::fail;
4use nom8::combinator::peek;
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(
19    check: RecursionCheck,
20) -> impl FnMut(Input<'_>) -> IResult<Input<'_>, v::Value, ParserError<'_>> {
21    move |input| {
22        dispatch!{peek(any);
23            crate::parser::strings::QUOTATION_MARK |
24            crate::parser::strings::APOSTROPHE => string.map(|s| {
25                v::Value::String(Formatted::new(
26                    s.into_owned()
27                ))
28            }),
29            crate::parser::array::ARRAY_OPEN => array(check).map(v::Value::Array),
30            crate::parser::inline_table::INLINE_TABLE_OPEN => inline_table(check).map(v::Value::InlineTable),
31            // Date/number starts
32            b'+' | b'-' | b'0'..=b'9' => {
33                // Uncommon enough not to be worth optimizing at this time
34                alt((
35                    date_time
36                        .map(v::Value::from),
37                    float
38                        .map(v::Value::from),
39                    integer
40                        .map(v::Value::from),
41                ))
42            },
43            // Report as if they were numbers because its most likely a typo
44            b'_' => {
45                    integer
46                        .map(v::Value::from)
47                .context(Context::Expected(ParserValue::Description("leading digit")))
48            },
49            // Report as if they were numbers because its most likely a typo
50            b'.' =>  {
51                    float
52                        .map(v::Value::from)
53                .context(Context::Expected(ParserValue::Description("leading digit")))
54            },
55            b't' => {
56                crate::parser::numbers::true_.map(v::Value::from)
57                    .context(Context::Expression("string"))
58                    .context(Context::Expected(ParserValue::CharLiteral('"')))
59                    .context(Context::Expected(ParserValue::CharLiteral('\'')))
60            },
61            b'f' => {
62                crate::parser::numbers::false_.map(v::Value::from)
63                    .context(Context::Expression("string"))
64                    .context(Context::Expected(ParserValue::CharLiteral('"')))
65                    .context(Context::Expected(ParserValue::CharLiteral('\'')))
66            },
67            b'i' => {
68                crate::parser::numbers::inf.map(v::Value::from)
69                    .context(Context::Expression("string"))
70                    .context(Context::Expected(ParserValue::CharLiteral('"')))
71                    .context(Context::Expected(ParserValue::CharLiteral('\'')))
72            },
73            b'n' => {
74                crate::parser::numbers::nan.map(v::Value::from)
75                    .context(Context::Expression("string"))
76                    .context(Context::Expected(ParserValue::CharLiteral('"')))
77                    .context(Context::Expected(ParserValue::CharLiteral('\'')))
78            },
79            _ => {
80                fail
81                    .context(Context::Expression("string"))
82                    .context(Context::Expected(ParserValue::CharLiteral('"')))
83                    .context(Context::Expected(ParserValue::CharLiteral('\'')))
84            },
85    }
86        .with_span()
87        .map_res(|(value, span)| apply_raw(value, span))
88        .parse(input)
89    }
90}
91
92fn apply_raw(mut val: Value, span: std::ops::Range<usize>) -> Result<Value, std::str::Utf8Error> {
93    match val {
94        Value::String(ref mut f) => {
95            let raw = RawString::with_span(span);
96            f.set_repr_unchecked(Repr::new_unchecked(raw));
97        }
98        Value::Integer(ref mut f) => {
99            let raw = RawString::with_span(span);
100            f.set_repr_unchecked(Repr::new_unchecked(raw));
101        }
102        Value::Float(ref mut f) => {
103            let raw = RawString::with_span(span);
104            f.set_repr_unchecked(Repr::new_unchecked(raw));
105        }
106        Value::Boolean(ref mut f) => {
107            let raw = RawString::with_span(span);
108            f.set_repr_unchecked(Repr::new_unchecked(raw));
109        }
110        Value::Datetime(ref mut f) => {
111            let raw = RawString::with_span(span);
112            f.set_repr_unchecked(Repr::new_unchecked(raw));
113        }
114        Value::Array(ref mut arr) => {
115            arr.span = Some(span);
116        }
117        Value::InlineTable(ref mut table) => {
118            table.span = Some(span);
119        }
120    };
121    val.decorate("", "");
122    Ok(val)
123}
124
125#[cfg(test)]
126mod test {
127    use super::*;
128
129    #[test]
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#"'''
138The first newline is
139trimmed 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)).finish();
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}