1use crate::{pbuf::PBytes, ValArray, Value};
2use arcstr::ArcStr;
3use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
4use bytes::Bytes;
5use combine::{
6 attempt, between, choice, from_str, many1, none_of, not_followed_by, one_of,
7 optional, parser,
8 parser::{
9 char::{alpha_num, digit, spaces, string},
10 combinator::recognize,
11 range::{take_while, take_while1},
12 repeat::escaped,
13 },
14 sep_by,
15 stream::{position, Range},
16 token, unexpected_any, EasyParser, ParseError, Parser, RangeStream,
17};
18use compact_str::CompactString;
19use escaping::Escape;
20use poolshark::local::LPooled;
21use rust_decimal::Decimal;
22use std::{borrow::Cow, result::Result, str::FromStr, sync::LazyLock, time::Duration};
23use triomphe::Arc;
24
25fn should_escape_generic(c: char) -> bool {
26 c.is_control()
27}
28
29pub const VAL_MUST_ESC: [char; 2] = ['\\', '"'];
30pub static VAL_ESC: LazyLock<Escape> = LazyLock::new(|| {
31 Escape::new(
32 '\\',
33 &['\\', '"', '\n', '\r', '\0', '\t'],
34 &[('\n', "n"), ('\r', "r"), ('\t', "t"), ('\0', "0")],
35 Some(should_escape_generic),
36 )
37 .unwrap()
38});
39
40pub fn escaped_string<I>(
41 must_esc: &'static [char],
42 esc: &Escape,
43) -> impl Parser<I, Output = String>
44where
45 I: RangeStream<Token = char>,
46 I::Error: ParseError<I::Token, I::Range, I::Position>,
47 I::Range: Range,
48{
49 recognize(escaped(
50 take_while1(move |c| !must_esc.contains(&c)),
51 esc.get_escape_char(),
52 one_of(
53 esc.get_tr()
54 .iter()
55 .filter_map(|(_, s)| s.chars().next())
56 .chain(must_esc.iter().copied()),
57 ),
58 ))
59 .map(|s| match esc.unescape(&s) {
60 Cow::Borrowed(_) => s, Cow::Owned(s) => s,
62 })
63}
64
65fn quoted<I>(
66 must_escape: &'static [char],
67 esc: &Escape,
68) -> impl Parser<I, Output = String>
69where
70 I: RangeStream<Token = char>,
71 I::Error: ParseError<I::Token, I::Range, I::Position>,
72 I::Range: Range,
73{
74 spaces().with(between(token('"'), token('"'), escaped_string(must_escape, esc)))
75}
76
77fn uint<I, T: FromStr + Clone + Copy>() -> impl Parser<I, Output = T>
78where
79 I: RangeStream<Token = char>,
80 I::Error: ParseError<I::Token, I::Range, I::Position>,
81 I::Range: Range,
82{
83 many1(digit()).then(|s: CompactString| match s.parse::<T>() {
84 Ok(i) => combine::value(i).right(),
85 Err(_) => unexpected_any("invalid unsigned integer").left(),
86 })
87}
88
89pub fn int<I, T: FromStr + Clone + Copy>() -> impl Parser<I, Output = T>
90where
91 I: RangeStream<Token = char>,
92 I::Error: ParseError<I::Token, I::Range, I::Position>,
93 I::Range: Range,
94{
95 recognize((optional(token('-')), take_while1(|c: char| c.is_digit(10)))).then(
96 |s: CompactString| match s.parse::<T>() {
97 Ok(i) => combine::value(i).right(),
98 Err(_) => unexpected_any("invalid signed integer").left(),
99 },
100 )
101}
102
103fn flt<I, T: FromStr + Clone + Copy>() -> impl Parser<I, Output = T>
104where
105 I: RangeStream<Token = char>,
106 I::Error: ParseError<I::Token, I::Range, I::Position>,
107 I::Range: Range,
108{
109 choice((
110 attempt(recognize((
111 optional(token('-')),
112 take_while1(|c: char| c.is_digit(10)),
113 optional(token('.')),
114 take_while(|c: char| c.is_digit(10)),
115 token('e'),
116 optional(token('-')),
117 take_while1(|c: char| c.is_digit(10)),
118 ))),
119 attempt(recognize((
120 optional(token('-')),
121 take_while1(|c: char| c.is_digit(10)),
122 token('.'),
123 take_while(|c: char| c.is_digit(10)),
124 ))),
125 ))
126 .then(|s: CompactString| match s.parse::<T>() {
127 Ok(i) => combine::value(i).right(),
128 Err(_) => unexpected_any("invalid float").left(),
129 })
130}
131
132struct Base64Encoded(Vec<u8>);
133
134impl FromStr for Base64Encoded {
135 type Err = base64::DecodeError;
136
137 fn from_str(s: &str) -> Result<Self, Self::Err> {
138 BASE64.decode(s).map(Base64Encoded)
139 }
140}
141
142fn base64str<I>() -> impl Parser<I, Output = String>
143where
144 I: RangeStream<Token = char>,
145 I::Error: ParseError<I::Token, I::Range, I::Position>,
146 I::Range: Range,
147{
148 recognize((
149 take_while(|c: char| c.is_ascii_alphanumeric() || c == '+' || c == '/'),
150 take_while(|c: char| c == '='),
151 ))
152}
153
154fn constant<I>(typ: &'static str) -> impl Parser<I, Output = char>
155where
156 I: RangeStream<Token = char>,
157 I::Error: ParseError<I::Token, I::Range, I::Position>,
158 I::Range: Range,
159{
160 string(typ).with(token(':'))
161}
162
163pub fn close_expr<I>() -> impl Parser<I, Output = ()>
164where
165 I: RangeStream<Token = char>,
166 I::Error: ParseError<I::Token, I::Range, I::Position>,
167 I::Range: Range,
168{
169 not_followed_by(none_of([' ', '\n', '\t', ';', ')', ',', ']', '}', '"']))
170}
171
172fn value_<I>(must_escape: &'static [char], esc: &Escape) -> impl Parser<I, Output = Value>
173where
174 I: RangeStream<Token = char>,
175 I::Error: ParseError<I::Token, I::Range, I::Position>,
176 I::Range: Range,
177{
178 spaces().with(choice((
179 attempt(
180 between(
181 token('['),
182 token(']'),
183 sep_by(value(must_escape, esc), attempt(spaces().with(token(',')))),
184 )
185 .map(|mut vals: LPooled<Vec<Value>>| {
186 Value::Array(ValArray::from_iter_exact(vals.drain(..)))
187 }),
188 ),
189 attempt(between(
190 token('{'),
191 token('}'),
192 sep_by(
193 (
194 value(must_escape, esc),
195 spaces().with(string("=>")).with(value(must_escape, esc)),
196 ),
197 attempt(spaces().with(token(','))),
198 )
199 .map(|mut vals: LPooled<Vec<(Value, Value)>>| {
200 Value::Map(immutable_chunkmap::map::Map::from_iter(vals.drain(..)))
201 }),
202 )),
203 attempt(quoted(must_escape, esc)).map(|s| Value::String(ArcStr::from(s))),
204 attempt(flt::<_, f64>()).map(Value::F64),
205 attempt(int::<_, i64>()).map(Value::I64),
206 attempt(
207 string("true").skip(not_followed_by(alpha_num())).map(|_| Value::Bool(true)),
208 ),
209 attempt(
210 string("false")
211 .skip(not_followed_by(alpha_num()))
212 .map(|_| Value::Bool(false)),
213 ),
214 attempt(string("null").skip(not_followed_by(alpha_num())).map(|_| Value::Null)),
215 attempt(
216 constant("decimal")
217 .with(flt::<_, Decimal>())
218 .map(|d| Value::Decimal(Arc::new(d))),
219 ),
220 attempt(constant("u32").with(uint::<_, u32>()).map(Value::U32)),
221 attempt(constant("v32").with(uint::<_, u32>()).map(Value::V32)),
222 attempt(constant("i32").with(int::<_, i32>()).map(Value::I32)),
223 attempt(constant("z32").with(int::<_, i32>()).map(Value::Z32)),
224 attempt(constant("u64").with(uint::<_, u64>()).map(Value::U64)),
225 attempt(constant("v64").with(uint::<_, u64>()).map(Value::V64)),
226 attempt(constant("i64").with(int::<_, i64>()).map(Value::I64)),
227 attempt(constant("z64").with(int::<_, i64>()).map(Value::Z64)),
228 attempt(constant("f32").with(flt::<_, f32>()).map(Value::F32)),
229 attempt(constant("f64").with(flt::<_, f64>()).map(Value::F64)),
230 attempt(
231 constant("bytes")
232 .with(from_str(base64str()))
233 .map(|Base64Encoded(v)| Value::Bytes(PBytes::new(Bytes::from(v)))),
234 ),
235 attempt(
236 constant("error")
237 .with(value(must_escape, esc))
238 .map(|v| Value::Error(Arc::new(v))),
239 ),
240 attempt(
241 constant("datetime")
242 .with(from_str(quoted(must_escape, esc)))
243 .map(|d| Value::DateTime(Arc::new(d))),
244 ),
245 attempt(
246 constant("duration")
247 .with(flt::<_, f64>().and(choice((
248 string("ns"),
249 string("us"),
250 string("ms"),
251 string("s"),
252 ))))
253 .map(|(n, suffix)| {
254 let d = match suffix {
255 "ns" => Duration::from_secs_f64(n / 1e9),
256 "us" => Duration::from_secs_f64(n / 1e6),
257 "ms" => Duration::from_secs_f64(n / 1e3),
258 "s" => Duration::from_secs_f64(n),
259 _ => unreachable!(),
260 };
261 Value::Duration(Arc::new(d))
262 }),
263 ),
264 )))
265}
266
267parser! {
268 pub fn value['a, I](
269 must_escape: &'static [char],
270 esc: &'a Escape
271 )(I) -> Value
272 where [I: RangeStream<Token = char>, I::Range: Range]
273 {
274 value_(must_escape, esc)
275 }
276}
277
278pub fn parse_value(s: &str) -> anyhow::Result<Value> {
279 value(&VAL_MUST_ESC, &VAL_ESC)
280 .easy_parse(position::Stream::new(s))
281 .map(|(r, _)| r)
282 .map_err(|e| anyhow::anyhow!(format!("{}", e)))
283}
284
285#[cfg(test)]
286mod tests {
287 use arcstr::literal;
288
289 use super::*;
290
291 #[test]
292 fn parse() {
293 assert_eq!(Value::U32(23), parse_value("u32:23").unwrap());
294 assert_eq!(Value::V32(42), parse_value("v32:42").unwrap());
295 assert_eq!(Value::I32(-10), parse_value("i32:-10").unwrap());
296 assert_eq!(Value::I32(12321), parse_value("i32:12321").unwrap());
297 assert_eq!(Value::Z32(-99), parse_value("z32:-99").unwrap());
298 assert_eq!(Value::U64(100), parse_value("u64:100").unwrap());
299 assert_eq!(Value::V64(100), parse_value("v64:100").unwrap());
300 assert_eq!(Value::I64(-100), parse_value("i64:-100").unwrap());
301 assert_eq!(Value::I64(-100), parse_value("-100").unwrap());
302 assert_eq!(Value::I64(100), parse_value("i64:100").unwrap());
303 assert_eq!(Value::I64(100), parse_value("100").unwrap());
304 assert_eq!(Value::Z64(-100), parse_value("z64:-100").unwrap());
305 assert_eq!(Value::Z64(100), parse_value("z64:100").unwrap());
306 assert_eq!(Value::F32(3.1415), parse_value("f32:3.1415").unwrap());
307 assert_eq!(Value::F32(675.6), parse_value("f32:675.6").unwrap());
308 assert_eq!(Value::F32(42.3435), parse_value("f32:42.3435").unwrap());
309 assert_eq!(Value::F32(1.123e9), parse_value("f32:1.123e9").unwrap());
310 assert_eq!(Value::F32(1e9), parse_value("f32:1e9").unwrap());
311 assert_eq!(Value::F32(21.2443e-6), parse_value("f32:21.2443e-6").unwrap());
312 assert_eq!(Value::F32(3.), parse_value("f32:3.").unwrap());
313 assert_eq!(Value::F64(3.1415), parse_value("f64:3.1415").unwrap());
314 assert_eq!(Value::F64(3.1415), parse_value("3.1415").unwrap());
315 assert_eq!(Value::F64(1.123e9), parse_value("1.123e9").unwrap());
316 assert_eq!(Value::F64(1e9), parse_value("1e9").unwrap());
317 assert_eq!(Value::F64(21.2443e-6), parse_value("21.2443e-6").unwrap());
318 assert_eq!(Value::F64(3.), parse_value("f64:3.").unwrap());
319 assert_eq!(Value::F64(3.), parse_value("3.").unwrap());
320 let c = ArcStr::from(r#"I've got a lovely "bunch" of (coconuts)"#);
321 let s = r#""I've got a lovely \"bunch\" of (coconuts)""#;
322 assert_eq!(Value::String(c), parse_value(s).unwrap());
323 let c = ArcStr::new();
324 assert_eq!(Value::String(c), parse_value(r#""""#).unwrap());
325 let c = ArcStr::from(r#"""#);
326 let s = r#""\"""#;
327 assert_eq!(Value::String(c), parse_value(s).unwrap());
328 assert_eq!(Value::Bool(true), parse_value("true").unwrap());
329 assert_eq!(Value::Bool(true), parse_value("true ").unwrap());
330 assert_eq!(Value::Bool(false), parse_value("false").unwrap());
331 assert_eq!(Value::Null, parse_value("null").unwrap());
332 assert_eq!(
333 Value::error(literal!("error")),
334 parse_value(r#"error:"error""#).unwrap()
335 );
336 }
337}