Commit 07cfed6c authored by Éloïs's avatar Éloïs

[feat] add crate lib/tools/json-pest-parser

parent 7b8b6bc9
......@@ -249,8 +249,8 @@ dependencies = [
"base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"dup-crypto 0.5.0",
"pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"pest_derive 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
......@@ -407,8 +407,8 @@ dependencies = [
"dubp-documents 0.11.0",
"dup-crypto 0.5.0",
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"pest_derive 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
......@@ -638,6 +638,16 @@ name = "itoa"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "json-pest-parser"
version = "0.1.0"
dependencies = [
"maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
......@@ -823,7 +833,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "pest"
version = "2.0.2"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ucd-trie 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
......@@ -831,32 +841,32 @@ dependencies = [
[[package]]
name = "pest_derive"
version = "2.0.1"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"pest_generator 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pest_generator 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pest_generator"
version = "2.0.0"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"pest_meta 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pest_meta 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pest_meta"
version = "2.0.3"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"sha-1 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
......@@ -1153,16 +1163,6 @@ dependencies = [
"syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "0.14.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "0.15.22"
......@@ -1415,10 +1415,10 @@ dependencies = [
"checksum openssl-sys 0.9.39 (registry+https://github.com/rust-lang/crates.io-index)" = "278c1ad40a89aa1e741a1eed089a2f60b18fab8089c3139b542140fc7d674106"
"checksum pbr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "deb73390ab68d81992bd994d145f697451bb0b54fd39738e72eef32458ad6907"
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
"checksum pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a677051ad923732bb5c70f2d45f8985a96e3eee2e2bff86697e3b11b0c3fcfde"
"checksum pest_derive 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b76f477146419bc539a63f4ef40e902166cb43b3e51cecc71d9136fd12c567e7"
"checksum pest_generator 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ebee4e9680be4fd162e6f3394ae4192a6b60b1e4d17d845e631f0c68d1a3386"
"checksum pest_meta 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1f6d5f6f0e6082578c86af197d780dc38328e3f768cec06aac9bc46d714e8221"
"checksum pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "54f0c72a98d8ab3c99560bfd16df8059cc10e1f9a8e83e6e3b97718dd766e9c3"
"checksum pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
"checksum pest_generator 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "63120576c4efd69615b5537d3d052257328a4ca82876771d6944424ccfd9f646"
"checksum pest_meta 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5a3492a4ed208ffc247adcdcc7ba2a95be3104f58877d0d02f0df39bf3efb5e"
"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c"
"checksum pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a029430f0d744bc3d15dd474d591bed2402b645d024583082b9f63bb936dac6"
"checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09"
......@@ -1455,7 +1455,6 @@ dependencies = [
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
"checksum structopt 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "41c4a2479a078509940d82773d90ff824a8c89533ab3b59cd3ce8b0c0e369c02"
"checksum structopt-derive 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "5352090cfae7a2c85e1a31146268b53396106c88ca5d6ccee2e3fae83b6e35c2"
"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741"
"checksum syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)" = "ae8b29eb5210bc5cf63ed6149cbf9adfc82ac0be023d8735c176ee74a2db4da7"
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
"checksum term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e6b677dd1e8214ea1ef4297f85dbcbed8e8cdddb561040cc998ca2551c37561"
......
......@@ -15,6 +15,7 @@ members = [
"lib/modules/ws2p/ws2p-messages",
"lib/tools/crypto",
"lib/tools/documents",
"lib/tools/json-pest-parser",
"lib/tools/network-documents",
"lib/tools/wot",
]
[package]
name = "json-pest-parser"
version = "0.1.0"
authors = ["elois <elois@duniter.org>"]
description = "Parse JSON with pest grammar."
repository = "https://git.duniter.org/nodes/rust/duniter-rs"
readme = "README.md"
keywords = ["json", "pest", "parser"]
license = "AGPL-3.0"
edition = "2018"
[lib]
path = "src/lib.rs"
[dependencies]
pest = "2.1.0"
pest_derive = "2.1.0"
[dev-dependencies]
pretty_assertions = "0.5.1"
maplit = "1.0.1"
\ No newline at end of file
WHITESPACE = _{ " " | "\t" | "\r" | "\n" }
null = { "null" }
boolean = { "true" | "false" }
number = @{
"-"?
~ ("0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*)
~ ("." ~ ASCII_DIGIT*)?
~ (^"e" ~ ("+" | "-")? ~ ASCII_DIGIT+)?
}
string = ${ "\"" ~ inner_string ~ "\"" }
inner_string = @{ char* }
char = {
!("\"" | "\\") ~ ANY
| "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t")
| "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4})
}
object = {
"{" ~ "}" |
"{" ~ pair ~ ("," ~ pair)* ~ "}"
}
pair = { string ~ ":" ~ value }
array = {
"[" ~ "]" |
"[" ~ value ~ ("," ~ value)* ~ "]"
}
value = _{ object | array | string | number | boolean | null }
json = _{ SOI ~ (object | array) ~ EOI }
// Copyright (C) 2019 Éloïs SANCHEZ
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Parse JSON String.
#![deny(
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces
)]
#[macro_use]
extern crate pest_derive;
#[cfg(test)]
#[macro_use]
extern crate maplit;
#[cfg(test)]
#[macro_use]
extern crate pretty_assertions;
use pest::error::Error;
use pest::iterators::Pair;
use pest::Parser;
use std::collections::HashMap;
#[derive(Parser)]
#[grammar = "json_grammar.pest"]
struct JSONParser;
#[derive(Debug, PartialEq)]
pub enum JSONValue<'a> {
Object(HashMap<&'a str, JSONValue<'a>>),
Array(Vec<JSONValue<'a>>),
String(&'a str),
Number(f64),
Boolean(bool),
Null,
}
impl<'a> JSONValue<'a> {
pub fn is_object(&self) -> bool {
if let JSONValue::Object(_) = self {
true
} else {
false
}
}
pub fn to_object(&self) -> Option<&HashMap<&'a str, JSONValue<'a>>> {
if let JSONValue::Object(object) = self {
Some(object)
} else {
None
}
}
pub fn is_array(&self) -> bool {
if let JSONValue::Array(_) = self {
true
} else {
false
}
}
pub fn to_array(&self) -> Option<&Vec<JSONValue<'a>>> {
if let JSONValue::Array(array) = self {
Some(array)
} else {
None
}
}
pub fn is_str(&self) -> bool {
if let JSONValue::String(_) = self {
true
} else {
false
}
}
pub fn to_str(&self) -> Option<&'a str> {
if let JSONValue::String(string) = self {
Some(string)
} else {
None
}
}
pub fn is_number(&self) -> bool {
if let JSONValue::Number(_) = self {
true
} else {
false
}
}
pub fn to_number(&self) -> Option<f64> {
if let JSONValue::Number(number) = self {
Some(*number)
} else {
None
}
}
pub fn is_bool(&self) -> bool {
if let JSONValue::Boolean(_) = self {
true
} else {
false
}
}
pub fn to_bool(&self) -> Option<bool> {
if let JSONValue::Boolean(boolean) = self {
Some(*boolean)
} else {
None
}
}
pub fn is_null(&self) -> bool {
if let JSONValue::Null = self {
true
} else {
false
}
}
}
impl<'a> ToString for JSONValue<'a> {
fn to_string(&self) -> String {
match self {
JSONValue::Object(o) => {
let contents: Vec<_> = o
.iter()
.map(|(name, value)| format!("\"{}\":{}", name, value.to_string()))
.collect();
format!("{{{}}}", contents.join(","))
}
JSONValue::Array(a) => {
let contents: Vec<_> = a.iter().map(Self::to_string).collect();
format!("[{}]", contents.join(","))
}
JSONValue::String(s) => format!("\"{}\"", s),
JSONValue::Number(n) => format!("{}", n),
JSONValue::Boolean(b) => format!("{}", b),
JSONValue::Null => "null".to_owned(),
}
}
}
pub fn parse_json_string(source: &str) -> Result<JSONValue, Error<Rule>> {
let json = JSONParser::parse(Rule::json, source)?.next().unwrap();
Ok(parse_value(json))
}
fn parse_value(pair: Pair<Rule>) -> JSONValue {
match pair.as_rule() {
Rule::object => JSONValue::Object(
pair.into_inner()
.map(|pair| {
let mut inner_rules = pair.into_inner();
let name = inner_rules
.next()
.unwrap()
.into_inner()
.next()
.unwrap()
.as_str();
let value = parse_value(inner_rules.next().unwrap());
(name, value)
})
.collect(),
),
Rule::array => JSONValue::Array(pair.into_inner().map(parse_value).collect()),
Rule::string => JSONValue::String(pair.into_inner().next().unwrap().as_str()),
Rule::number => JSONValue::Number(pair.as_str().parse().unwrap()),
Rule::boolean => JSONValue::Boolean(pair.as_str().parse().unwrap()),
Rule::null => JSONValue::Null,
Rule::json
| Rule::EOI
| Rule::pair
| Rule::value
| Rule::inner_string
| Rule::char
| Rule::WHITESPACE => unreachable!(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_json_string() {
let json_string = "{
\"name\": \"toto\",
\"age\": 25,
\"friends\": [
\"titi\",
\"tata\"
]
}";
let json_value = parse_json_string(json_string).expect("Fail to parse json string !");
assert_eq!(
JSONValue::Object(hashmap![
"name" => JSONValue::String("toto"),
"age" => JSONValue::Number(25f64),
"friends" => JSONValue::Array(vec![JSONValue::String("titi"), JSONValue::String("tata"),])
]),
json_value
);
assert!(json_value.is_object());
let json_object = json_value.to_object().expect("safe unwrap");
assert_eq!(json_object.get("name"), Some(&JSONValue::String("toto")));
assert_eq!(json_object.get("age"), Some(&JSONValue::Number(25f64)));
let friends = json_object
.get("friends")
.expect("frinds field must be exist")
.to_array()
.expect("frinds field must be an array");
assert_eq!(2, friends.len());
assert_eq!(
"titi",
friends[0]
.to_str()
.expect("friends field must be an array of String")
);
assert_eq!(
"tata",
friends[1]
.to_str()
.expect("friends field must be an array of String")
);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment