1#![deny(
21 clippy::unwrap_used,
22 missing_debug_implementations,
23 missing_copy_implementations,
24 trivial_casts,
25 trivial_numeric_casts,
26 unsafe_code,
27 unstable_features,
28 unused_import_braces
29)]
30
31#[macro_use]
32extern crate pest_derive;
33
34use pest::iterators::Pair;
35use pest::Parser;
36use std::collections::HashMap;
37use std::str::FromStr;
38use thiserror::Error;
39use unwrap::unwrap;
40
41#[derive(Parser)]
42#[grammar = "json_grammar.pest"]
43struct JSONParser;
44
45#[derive(Debug, PartialEq)]
46pub enum JSONValue<'a, S: std::hash::BuildHasher> {
47 Object(HashMap<&'a str, JSONValue<'a, S>, S>),
48 Array(Vec<JSONValue<'a, S>>),
49 String(&'a str),
50 Number(Number),
51 Boolean(bool),
52 Null,
53}
54
55#[derive(Copy, Clone, Debug, PartialEq)]
56pub enum Number {
57 F64(f64),
58 U64(u64),
59}
60
61type JsonObject<'a, S> = HashMap<&'a str, JSONValue<'a, S>, S>;
62
63impl<'a, S: std::hash::BuildHasher> JSONValue<'a, S> {
64 pub fn is_object(&self) -> bool {
65 if let JSONValue::Object(_) = self {
66 true
67 } else {
68 false
69 }
70 }
71
72 pub fn to_object(&self) -> Option<&HashMap<&'a str, JSONValue<'a, S>, S>> {
73 if let JSONValue::Object(object) = self {
74 Some(object)
75 } else {
76 None
77 }
78 }
79
80 pub fn is_array(&self) -> bool {
81 if let JSONValue::Array(_) = self {
82 true
83 } else {
84 false
85 }
86 }
87
88 pub fn to_array(&self) -> Option<&Vec<JSONValue<'a, S>>> {
89 if let JSONValue::Array(array) = self {
90 Some(array)
91 } else {
92 None
93 }
94 }
95
96 pub fn is_str(&self) -> bool {
97 if let JSONValue::String(_) = self {
98 true
99 } else {
100 false
101 }
102 }
103
104 pub fn to_str(&self) -> Option<&'a str> {
105 if let JSONValue::String(string) = self {
106 Some(string)
107 } else {
108 None
109 }
110 }
111
112 pub fn is_number(&self) -> bool {
113 if let JSONValue::Number(_) = self {
114 true
115 } else {
116 false
117 }
118 }
119
120 pub fn to_f64(&self) -> Option<f64> {
121 if let JSONValue::Number(number) = self {
122 match number {
123 Number::F64(f64_) => Some(*f64_),
124 Number::U64(u64_) => Some(*u64_ as f64),
125 }
126 } else {
127 None
128 }
129 }
130
131 pub fn to_u64(&self) -> Option<u64> {
132 if let JSONValue::Number(number) = self {
133 if let Number::U64(u64_) = number {
134 Some(*u64_)
135 } else {
136 None
137 }
138 } else {
139 None
140 }
141 }
142
143 pub fn is_bool(&self) -> bool {
144 if let JSONValue::Boolean(_) = self {
145 true
146 } else {
147 false
148 }
149 }
150
151 pub fn to_bool(&self) -> Option<bool> {
152 if let JSONValue::Boolean(boolean) = self {
153 Some(*boolean)
154 } else {
155 None
156 }
157 }
158
159 pub fn is_null(&self) -> bool {
160 if let JSONValue::Null = self {
161 true
162 } else {
163 false
164 }
165 }
166}
167
168impl<'a, S: std::hash::BuildHasher> ToString for JSONValue<'a, S> {
169 fn to_string(&self) -> String {
170 match self {
171 JSONValue::Object(o) => {
172 let contents: Vec<_> = o
173 .iter()
174 .map(|(name, value)| format!("\"{}\":{}", name, value.to_string()))
175 .collect();
176 format!("{{{}}}", contents.join(","))
177 }
178 JSONValue::Array(a) => {
179 let contents: Vec<_> = a.iter().map(Self::to_string).collect();
180 format!("[{}]", contents.join(","))
181 }
182 JSONValue::String(s) => format!("\"{}\"", s),
183 JSONValue::Number(n) => match n {
184 Number::F64(f64_) => format!("{}", f64_),
185 Number::U64(u64_) => format!("{}", u64_),
186 },
187 JSONValue::Boolean(b) => format!("{}", b),
188 JSONValue::Null => "null".to_owned(),
189 }
190 }
191}
192
193#[derive(Debug, Error)]
194#[error("Fail to parse JSON String : {:?}", cause)]
195pub struct ParseJsonError {
196 pub cause: String,
197}
198
199pub fn parse_json_string<'a>(
200 source: &'a str,
201) -> Result<
202 JSONValue<'a, std::hash::BuildHasherDefault<std::collections::hash_map::DefaultHasher>>,
203 ParseJsonError,
204> {
205 parse_json_string_with_specific_hasher::<
206 std::hash::BuildHasherDefault<std::collections::hash_map::DefaultHasher>,
207 >(source)
208}
209
210pub fn parse_json_string_with_specific_hasher<S: std::hash::BuildHasher + Default>(
211 source: &str,
212) -> Result<JSONValue<S>, ParseJsonError> {
213 match JSONParser::parse(Rule::json, source) {
214 Ok(mut pair) => Ok(parse_value(unwrap!(
215 pair.next(),
216 "Fail to parse Rule::json"
217 ))),
218 Err(pest_error) => Err(ParseJsonError {
219 cause: format!("{:?}", pest_error),
220 }),
221 }
222}
223
224fn parse_value<S: std::hash::BuildHasher + Default>(pair: Pair<Rule>) -> JSONValue<S> {
225 match pair.as_rule() {
226 Rule::object => JSONValue::Object(
227 pair.into_inner()
228 .map(|pair| {
229 let mut inner_rules = pair.into_inner();
230 let name = unwrap!(
231 unwrap!(inner_rules.next(), "Fail to parse Rule::object::name")
232 .into_inner()
233 .next(),
234 "Fail to parse Rule::object::name"
235 )
236 .as_str();
237 let value = parse_value(unwrap!(
238 inner_rules.next(),
239 "Fail to parse Rule::object::value"
240 ));
241 (name, value)
242 })
243 .collect(),
244 ),
245 Rule::array => JSONValue::Array(pair.into_inner().map(parse_value).collect()),
246 Rule::string => JSONValue::String(
247 unwrap!(pair.into_inner().next(), "Fail to parse Rule::string").as_str(),
248 ),
249 Rule::number => {
250 if let Ok(number_u64) = u64::from_str(pair.as_str()) {
251 JSONValue::Number(Number::U64(number_u64))
252 } else {
253 JSONValue::Number(Number::F64(unwrap!(
254 pair.as_str().parse(),
255 "Fail to parse Rule::number as u64 and f64"
256 )))
257 }
258 }
259 Rule::boolean => JSONValue::Boolean(unwrap!(
260 pair.as_str().parse(),
261 "Fail to parse Rule::boolean"
262 )),
263 Rule::null => JSONValue::Null,
264 Rule::json
265 | Rule::EOI
266 | Rule::pair
267 | Rule::value
268 | Rule::inner_string
269 | Rule::char
270 | Rule::WHITESPACE => unreachable!(),
271 }
272}
273
274pub fn get_optional_usize<S: std::hash::BuildHasher>(
275 json_block: &HashMap<&str, JSONValue<S>, S>,
276 field: &str,
277) -> Result<Option<usize>, ParseJsonError> {
278 Ok(match json_block.get(field) {
279 Some(value) => {
280 if !value.is_null() {
281 Some(
282 value
283 .to_f64()
284 .ok_or_else(|| ParseJsonError {
285 cause: format!(
286 "Fail to parse json : field '{}' must be a number !",
287 field
288 ),
289 })?
290 .trunc() as usize,
291 )
292 } else {
293 None
294 }
295 }
296 None => None,
297 })
298}
299
300pub fn get_optional_str_not_empty<'a, S: std::hash::BuildHasher>(
301 json_block: &'a HashMap<&str, JSONValue<S>, S>,
302 field: &str,
303) -> Result<Option<&'a str>, ParseJsonError> {
304 let result = get_optional_str(json_block, field);
305 if let Ok(Some(value)) = result {
306 if !value.is_empty() {
307 Ok(Some(value))
308 } else {
309 Ok(None)
310 }
311 } else {
312 result
313 }
314}
315
316pub fn get_optional_str<'a, S: std::hash::BuildHasher>(
317 json_block: &'a HashMap<&str, JSONValue<S>, S>,
318 field: &str,
319) -> Result<Option<&'a str>, ParseJsonError> {
320 Ok(match json_block.get(field) {
321 Some(value) => {
322 if !value.is_null() {
323 Some(value.to_str().ok_or_else(|| ParseJsonError {
324 cause: format!("Fail to parse json : field '{}' must be a string !", field),
325 })?)
326 } else {
327 None
328 }
329 }
330 None => None,
331 })
332}
333
334pub fn get_u64<S: std::hash::BuildHasher>(
335 json_block: &HashMap<&str, JSONValue<S>, S>,
336 field: &str,
337) -> Result<u64, ParseJsonError> {
338 Ok(json_block
339 .get(field)
340 .ok_or_else(|| ParseJsonError {
341 cause: format!("Fail to parse json : field '{}' must exist !", field),
342 })?
343 .to_u64()
344 .ok_or_else(|| ParseJsonError {
345 cause: format!("Fail to parse json : field '{}' must be a number !", field),
346 })?)
347}
348
349pub fn get_number<S: std::hash::BuildHasher>(
350 json_block: &HashMap<&str, JSONValue<S>, S>,
351 field: &str,
352) -> Result<f64, ParseJsonError> {
353 Ok(json_block
354 .get(field)
355 .ok_or_else(|| ParseJsonError {
356 cause: format!("Fail to parse json : field '{}' must exist !", field),
357 })?
358 .to_f64()
359 .ok_or_else(|| ParseJsonError {
360 cause: format!("Fail to parse json : field '{}' must be a number !", field),
361 })?)
362}
363
364pub fn get_str<'a, S: std::hash::BuildHasher>(
365 json_block: &'a HashMap<&str, JSONValue<S>, S>,
366 field: &str,
367) -> Result<&'a str, ParseJsonError> {
368 Ok(json_block
369 .get(field)
370 .ok_or_else(|| ParseJsonError {
371 cause: format!("Fail to parse json : field '{}' must exist !", field),
372 })?
373 .to_str()
374 .ok_or_else(|| ParseJsonError {
375 cause: format!("Fail to parse json : field '{}' must be a string !", field),
376 })?)
377}
378
379pub fn get_str_array<'a, S: std::hash::BuildHasher>(
380 json_block: &'a HashMap<&str, JSONValue<S>, S>,
381 field: &str,
382) -> Result<Vec<&'a str>, ParseJsonError> {
383 json_block
384 .get(field)
385 .ok_or_else(|| ParseJsonError {
386 cause: format!("Fail to parse json : field '{}' must exist !", field),
387 })?
388 .to_array()
389 .ok_or_else(|| ParseJsonError {
390 cause: format!("Fail to parse json : field '{}' must be an array !", field),
391 })?
392 .iter()
393 .map(|v| {
394 v.to_str().ok_or_else(|| ParseJsonError {
395 cause: format!(
396 "Fail to parse json : field '{}' must be an array of string !",
397 field
398 ),
399 })
400 })
401 .collect()
402}
403
404pub fn get_array<'a, S: std::hash::BuildHasher>(
405 json_block: &'a HashMap<&str, JSONValue<S>, S>,
406 field: &str,
407) -> Result<Vec<&'a JSONValue<'a, S>>, ParseJsonError> {
408 Ok(json_block
409 .get(field)
410 .ok_or_else(|| ParseJsonError {
411 cause: format!("Fail to parse json : field '{}' must exist !", field),
412 })?
413 .to_array()
414 .ok_or_else(|| ParseJsonError {
415 cause: format!("Fail to parse json : field '{}' must be an array !", field),
416 })?
417 .iter()
418 .map(|v| v)
419 .collect())
420}
421
422pub fn get_object_array<'a, S: std::hash::BuildHasher>(
423 json_block: &'a JsonObject<'a, S>,
424 field: &str,
425) -> Result<Vec<&'a JsonObject<'a, S>>, ParseJsonError> {
426 json_block
427 .get(field)
428 .ok_or_else(|| ParseJsonError {
429 cause: format!("Fail to parse json : field '{}' must exist !", field),
430 })?
431 .to_array()
432 .ok_or_else(|| ParseJsonError {
433 cause: format!("Fail to parse json : field '{}' must be an array !", field),
434 })?
435 .iter()
436 .map(|v| {
437 v.to_object().ok_or_else(|| ParseJsonError {
438 cause: format!("Fail to parse json : field '{}' must be an object !", field),
439 })
440 })
441 .collect()
442}
443
444#[cfg(test)]
445mod tests {
446 use super::*;
447 use pretty_assertions::assert_eq;
448
449 #[test]
450 fn test_parse_too_large_number() {
451 assert_eq!(
452 Ok(100_010_200_000_006_940),
453 u64::from_str("100010200000006940"),
454 );
455
456 let json_string = "{
457 \"nonce\": 100010200000006940
458 }";
459
460 let json_value = parse_json_string(json_string).expect("Fail to parse json string !");
461
462 assert!(json_value.is_object());
463
464 let json_object = json_value.to_object().expect("safe unwrap");
465
466 assert_eq!(
467 json_object.get("nonce"),
468 Some(&JSONValue::Number(Number::U64(100_010_200_000_006_940)))
469 );
470 }
471
472 #[test]
473 fn test_parse_wrong_json_string() {
474 let json_string = "{";
475 assert!(parse_json_string(json_string).is_err());
476 }
477
478 fn test_parse_json_string_check_object_type(
479 json_value: &JSONValue<
480 std::hash::BuildHasherDefault<std::collections::hash_map::DefaultHasher>,
481 >,
482 ) {
483 assert!(json_value.is_object());
484 assert!(!json_value.is_array());
485 assert!(!json_value.is_str());
486 assert!(!json_value.is_number());
487 assert!(!json_value.is_bool());
488 assert!(!json_value.is_null());
489 assert_eq!(None, json_value.to_array());
490 assert_eq!(None, json_value.to_str());
491 assert_eq!(None, json_value.to_f64());
492 assert_eq!(None, json_value.to_u64());
493 assert_eq!(None, json_value.to_bool());
494 }
495
496 #[test]
497 fn test_parse_json_string() {
498 let json_string = "{
499 \"name\": \"toto\",
500 \"age\": 25,
501 \"legalAge\": true,
502 \"ratio\": 0.5,
503 \"friends\": [
504 \"titi\",
505 \"tata\"
506 ],
507 \"car\": null
508 }";
509
510 let json_value = parse_json_string(json_string).expect("Fail to parse json string !");
511
512 assert_eq!(
513 json_value.to_string(),
514 "{\"name\":\"toto\",\"legalAge\":true,\"ratio\":0.5,\"age\":25,\"friends\":[\"titi\",\"tata\"],\"car\":null}"
515 );
516
517 test_parse_json_string_check_object_type(&json_value);
518
519 let json_object = json_value.to_object().expect("safe unwrap");
520
521 let name_field = json_object.get("name").expect("name field must be exist");
522 assert!(name_field.is_str());
523 assert_eq!(name_field, &JSONValue::String("toto"));
524
525 let age_field = json_object.get("age").expect("age field must be exist");
526 assert!(age_field.is_number());
527 assert_eq!(age_field.to_f64(), Some(25.0f64));
528 assert_eq!(age_field.to_u64(), Some(25u64));
529
530 let legal_age_field = json_object
531 .get("legalAge")
532 .expect("legalAge field must be exist");
533 assert!(legal_age_field.is_bool());
534 assert_eq!(legal_age_field.to_bool(), Some(true));
535
536 let ratio_field = json_object.get("ratio").expect("ratio field must be exist");
537 assert!(ratio_field.is_number());
538 assert_eq!(ratio_field.to_f64(), Some(0.5f64));
539 assert_eq!(ratio_field.to_u64(), None);
540
541 let friends_field = json_object
542 .get("friends")
543 .expect("friends field must be exist");
544 assert!(!friends_field.is_object());
545 assert_eq!(None, friends_field.to_object());
546 assert!(friends_field.is_array());
547
548 let friends = friends_field
549 .to_array()
550 .expect("frinds_field must be an array");
551
552 assert_eq!(2, friends.len());
553 assert_eq!(
554 "titi",
555 friends[0]
556 .to_str()
557 .expect("friends field must be an array of String")
558 );
559 assert_eq!(
560 "tata",
561 friends[1]
562 .to_str()
563 .expect("friends field must be an array of String")
564 );
565
566 let car_field = json_object.get("car").expect("car field must be exist");
567 assert!(car_field.is_null());
568 }
569}