jsnom/
lib.rs

1//! # JSnom
2//!
3//! A small and ergonomic parser library for JSON.
4//!
5//! All parsers provided silently discard any of the string remaining after parser finishes.
6//!
7//! ## Example
8//!
9//! ```
10//! use jsnom::JsonValue;
11//!
12//! assert_eq!(
13//!     JsonValue::from_str("[null, null, true]"),
14//!     Ok(JsonValue::Array(vec![
15//!         JsonValue::Null,
16//!         JsonValue::Null,
17//!         JsonValue::Bool(true)
18//!     ]))
19//! )
20//! ```
21
22use std::fmt;
23
24use nom::{
25    error::{convert_error, VerboseError, VerboseErrorKind},
26    Finish,
27};
28
29mod parse;
30
31/// Enum representing a parsed JSON input.
32#[derive(Clone, Debug, PartialEq)]
33pub enum JsonValue {
34    Null,
35    Bool(bool),
36    String(String),
37    Array(Vec<JsonValue>),
38    Number(f32),
39    Object(Vec<(JsonValue, JsonValue)>),
40}
41
42/// The error type returned from parsers. It is essentially a wrapper around
43/// [`nom::error::VerboseError`] using a different [`std::fmt::Display`].
44#[derive(Clone, Debug, PartialEq)]
45pub struct Error<'a> {
46    pub errors: Vec<(&'a str, VerboseErrorKind)>,
47    data: &'a str,
48    raw_error: VerboseError<&'a str>,
49}
50
51impl<'a> std::error::Error for Error<'a> {}
52
53impl<'a> fmt::Display for Error<'a> {
54    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
55        write!(f, "{}", convert_error(self.data, self.raw_error.clone()))
56    }
57}
58
59impl<'a> Error<'a> {
60    fn from_raw(data: &'a str, raw: VerboseError<&'a str>) -> Self {
61        Self {
62            errors: raw.clone().errors,
63            data,
64            raw_error: raw,
65        }
66    }
67}
68
69impl JsonValue {
70    /// Parse a [`JsonValue`] from an input string.
71    ///
72    /// ```
73    /// use jsnom::JsonValue;
74    ///
75    /// assert_eq!(
76    ///     JsonValue::from_str("[null, null, true]"),
77    ///     Ok(JsonValue::Array(vec![
78    ///         JsonValue::Null,
79    ///         JsonValue::Null,
80    ///         JsonValue::Bool(true)
81    ///     ]))
82    /// )
83    /// ```
84    #[allow(clippy::should_implement_trait)]
85    // We cannot implement `FromStr` due to lifetimes
86    pub fn from_str(s: &str) -> Result<Self, Error> {
87        parse(s)
88    }
89}
90
91/// Parse a [`JsonValue`] from an input string.
92pub fn parse(s: &str) -> Result<JsonValue, Error> {
93    parse::nom_parse(s)
94        .finish()
95        .map(|(_, val)| val)
96        .map_err(|e| Error::from_raw(s, e))
97}
98
99/// Parse a [`JsonValue::Null`] from an input string.
100///
101/// ```
102/// use jsnom::{parse_null, JsonValue};
103///
104/// assert_eq!(parse_null("null"), Ok(JsonValue::Null));
105/// ```
106pub fn parse_null(s: &str) -> Result<JsonValue, Error> {
107    parse::nom_null(s)
108        .finish()
109        .map(|(_, val)| val)
110        .map_err(|e| Error::from_raw(s, e))
111}
112
113/// Parse a [`JsonValue::Bool`] from an input string.
114/// ```
115/// use jsnom::{parse_bool, JsonValue};
116///
117/// assert_eq!(parse_bool("true"), Ok(JsonValue::Bool(true)));
118/// ```
119pub fn parse_bool(s: &str) -> Result<JsonValue, Error> {
120    parse::nom_bool(s)
121        .finish()
122        .map(|(_, val)| val)
123        .map_err(|e| Error::from_raw(s, e))
124}
125
126/// Parse a [`JsonValue::String`] from an input string.
127/// ```
128/// use jsnom::{parse_string, JsonValue};
129///
130/// assert_eq!(
131///     parse_string("\"Hello, world!\\n\""),
132///     Ok(JsonValue::String("Hello, world!\n".to_string()))
133/// );
134/// ```
135pub fn parse_string(s: &str) -> Result<JsonValue, Error> {
136    parse::nom_string(s)
137        .finish()
138        .map(|(_, val)| val)
139        .map_err(|e| Error::from_raw(s, e))
140}
141
142/// Parse a [`JsonValue::Array`] from an input string.
143/// ```
144/// use jsnom::{parse_array, JsonValue};
145///
146/// assert_eq!(
147///     parse_array("[null, null, [\"hello\", false]]"),
148///     Ok(JsonValue::Array(vec![
149///         JsonValue::Null,
150///         JsonValue::Null,
151///         JsonValue::Array(vec![
152///             JsonValue::String("hello".to_string()),
153///             JsonValue::Bool(false)
154///         ])
155///     ]))
156/// );
157/// ```
158pub fn parse_array(s: &str) -> Result<JsonValue, Error> {
159    parse::nom_array(s)
160        .finish()
161        .map(|(_, val)| val)
162        .map_err(|e| Error::from_raw(s, e))
163}
164
165/// Parse a [`JsonValue::Number`] from an input string.
166/// ```
167/// use jsnom::{parse_number, JsonValue};
168///
169/// assert_eq!(parse_number("-3e-2"), Ok(JsonValue::Number(-0.03)));
170/// ```
171pub fn parse_number(s: &str) -> Result<JsonValue, Error> {
172    parse::nom_number(s)
173        .finish()
174        .map(|(_, val)| val)
175        .map_err(|e| Error::from_raw(s, e))
176}
177
178/// Parse a [`JsonValue::Object`] from an input string.
179/// ```
180/// use jsnom::{parse_object, JsonValue::{self, *}};
181///
182/// assert_eq!(
183///     parse_object("{\"user\": \"Piturnah\", \"crates\": [\"gex\", \"newdoku\", \"jsnom\"]}"),
184///     Ok(JsonValue::Object(vec![
185///         (String("user".to_string()), String("Piturnah".to_string())),
186///         (String("crates".to_string()), Array(vec![
187///             String("gex".to_string()),
188///             String("newdoku".to_string()),
189///             String("jsnom".to_string()),
190///         ]))
191///     ])));
192/// ```
193pub fn parse_object(s: &str) -> Result<JsonValue, Error> {
194    parse::nom_object(s)
195        .finish()
196        .map(|(_, val)| val)
197        .map_err(|e| Error::from_raw(s, e))
198}