json_pop/
lib.rs

1pub mod extra;
2pub mod error;
3pub mod lex;
4
5use crate::error::CompilationError;
6use crate::lex::Token;
7
8pub use lalrpop_util;
9use logos::Logos;
10
11pub mod parser {
12    #![allow(clippy::all)]
13    use lalrpop_util::lalrpop_mod;
14    lalrpop_mod!(pub json);
15    use super::*;
16    pub use json::*;
17
18    pub type ParseError<'a> = lalrpop_util::ParseError<usize, Token<'a>, CompilationError>;
19    pub type ParseResult<'a> = Result<value::Value<'a>, ParseError<'a>>;
20    #[derive(Debug)]
21    pub struct Parsed<'a>(pub ParseResult<'a>);
22}
23
24pub mod value {
25    use lexical;
26    use std::fmt;
27
28    #[derive(Debug, Clone, PartialEq)]
29    pub enum Value<'a> {
30        Number(f64),
31        String(&'a str),
32        Object(Vec<(&'a str, Value<'a>)>),
33        Bool(bool),
34        Null,
35        Array(Vec<Value<'a>>),
36    }
37
38    impl<'a> fmt::Display for Value<'a> {
39        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40            match self {
41                Value::Number(float) => write!(f, "{}", lexical::to_string(*float)),
42                Value::String(string) => write!(f, "\"{}\"", string),
43                Value::Object(obj) => {
44                    write!(f, "{{")?;
45                    if let Some(((key, value), rest)) = obj.split_first() {
46                        write!(f, "\"{}\": {}", key, value)?;
47                        for (key, value) in rest.iter() {
48                            write!(f, ", \"{}\": {}", key, value)?
49                        }
50                    }
51                    write!(f, "}}")
52                }
53                Value::Bool(flag) => write!(f, "{}", flag),
54                Value::Null => write!(f, "null"),
55                Value::Array(array) => {
56                    write!(f, "[")?;
57                    if let Some((value, rest)) = array.split_first() {
58                        write!(f, "{}", value)?;
59                        for value in rest.iter() {
60                            write!(f, ", {}", value)?
61                        }
62                    }
63                    write!(f, "]")
64                }
65            }
66        }
67    }
68}
69
70pub fn parse_str<'a>(
71    bytes: &'a str,
72) -> std::result::Result<
73    value::Value<'a>,
74    lalrpop_util::ParseError<usize, Token<'a>, CompilationError>,
75> {
76    let lexer = Token::lexer(bytes).spanned().map(Token::to_lalr_triple);
77    parser::jsonParser::new().parse(lexer)
78}
79
80pub fn stringify<'a, W: std::io::Write>(w: &mut W, v: &'a value::Value<'a>) -> std::io::Result<()> {
81    write!(w, "{}", *v)
82}
83
84#[cfg(test)]
85mod test {
86    use super::*;
87    use crate::extra::source;
88    use source::ErrorHandling as _;
89    use source::Parsable as _;
90    use crate::extra::test_utils::Test;
91
92    #[test]
93    fn test_invalid() -> Result<(), error::TopLevelError> {
94        let sources = ["�", r#""string with missing end quote"#]
95            .iter()
96            .map(|src| Test::TestInvalid(src.into()));
97        Ok(for test in sources {
98            assert_eq!(
99                test.handle_errors(test.parse())
100                    .map_err(|e| error::TopLevelError::from(e))?,
101                crate::value::Value::Null
102            );
103        })
104    }
105
106    #[test]
107    fn test_valid() -> Result<(), error::TopLevelError> {
108        // The lifetimes here are kind of annoying in that we need to
109        // let bind these rather than just place them right in the array...
110        let empty_array = crate::value::Value::Array([].to_vec());
111        let string_value = crate::value::Value::String("foo bar");
112        let empty_string = crate::value::Value::String("");
113        let sources = [
114            ("[]", empty_array),
115            (r#""foo bar""#, string_value),
116            (r#""""#, empty_string),
117        ];
118        let tests = sources
119            .iter()
120            .map(|(src, result)| (Test::TestValid(src.into()), result));
121        Ok(for (test, result) in tests {
122            assert_eq!(
123                test.handle_errors(test.parse())
124                    .map_err(|e| error::TopLevelError::from(e))?,
125                *result
126            );
127        })
128    }
129}