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)]
124#[cfg(feature = "parse")]
125#[cfg(feature = "display")]
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