nparse/
indent_document.rs1use nom::{
2 branch::alt,
3 bytes::complete::{tag, take_till, take_till1},
4 character::complete::{char, line_ending},
5 combinator::{complete, eof, map, opt},
6 multi::{count, many0, many1},
7 sequence::{delimited, pair, separated_pair, terminated},
8 IResult,
9};
10use serde_json::{json, Value};
11
12pub(crate) fn parse_indent_string(i: &str) -> IResult<&str, Value> {
13 terminated(
14 map(opt(parse_value_array(0)), |v| v.unwrap_or_default()),
15 eof,
16 )(i)
17}
18
19fn parse_value_array(indent_level: usize) -> impl Fn(&str) -> IResult<&str, Value> {
20 move |i| {
21 map(
22 many1(complete(terminated(
23 alt((
24 parse_colon_pair(indent_level),
25 parse_value_pair_array(indent_level),
26 map(parse_string(indent_level), |s| (s, Value::Null)),
27 )),
28 many0(line_ending),
29 ))),
30 |mut s| {
31 if s.iter().any(|m| m.1.is_null()) {
32 json!(s
33 .drain(..)
34 .map(|(k, v)| if v.is_null() { json!(k) } else { json!({k: v}) })
35 .collect::<Vec<_>>())
36 } else {
37 json!(s
38 .drain(..)
39 .map(|(k, v)| (k.into(), v))
40 .collect::<serde_json::Map<_, _>>())
41 }
42 },
43 )(i)
44 }
45}
46
47fn parse_colon_pair(indent_level: usize) -> impl Fn(&str) -> IResult<&str, (&str, Value)> {
48 move |i| {
49 map(
50 delimited(
51 count(parse_indent, indent_level),
52 separated_pair(
53 take_till(|ch| ch == '\n' || ch == ':'),
54 pair(tag(":"), many1(char(' '))),
55 take_till1(|ch| ch == '\n' || ch == ':'),
56 ),
57 line_ending,
58 ),
59 |(k, v)| (k, json!(v)),
60 )(i)
61 }
62}
63
64fn parse_value_pair_array(indent_level: usize) -> impl Fn(&str) -> IResult<&str, (&str, Value)> {
65 move |i| {
66 pair(
67 parse_string(indent_level),
68 parse_value_array(indent_level + 1),
69 )(i)
70 }
71}
72
73fn parse_string(indent_level: usize) -> impl Fn(&str) -> IResult<&str, &str> {
74 move |i| {
75 delimited(
76 count(parse_indent, indent_level),
77 terminated(take_till(|ch| ch == '\n' || ch == ':'), opt(char(':'))),
78 line_ending,
79 )(i)
80 }
81}
82
83fn parse_indent(i: &str) -> IResult<&str, &str> {
84 alt((tag(" "), tag("\t")))(i)
85}