1use winnow::branch::alt;
2use winnow::bytes::any;
3use winnow::combinator::fail;
4use winnow::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 .try_map(|(value, span)| apply_raw(value, span))
88 .parse_next(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));
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