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}