From 65e533ee731eb392176b11dd2465223dacf7cc1e Mon Sep 17 00:00:00 2001 From: Philippe Assis Date: Fri, 27 Dec 2024 19:44:50 -0300 Subject: [PATCH 1/5] Bump version to 0.6.7 and update README badges --- Cargo.lock | 4 +- VERSION.txt | 2 +- valu3/Cargo.toml | 4 +- valu3/README.md | 6 +- valu3/src/serde_value/error.rs | 37 --------- valu3/src/serde_value/mod.rs | 145 +++++++++++++++++++++++++++++++++ valu3_derive/Cargo.toml | 2 +- valu3_derive/README.md | 6 +- 8 files changed, 157 insertions(+), 49 deletions(-) delete mode 100644 valu3/src/serde_value/error.rs diff --git a/Cargo.lock b/Cargo.lock index f17cb46..7ecab7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -437,7 +437,7 @@ checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" [[package]] name = "valu3" -version = "0.6.6" +version = "0.6.7" dependencies = [ "bincode", "chrono", @@ -452,7 +452,7 @@ dependencies = [ [[package]] name = "valu3-derive" -version = "0.6.6" +version = "0.6.7" dependencies = [ "proc-macro2", "quote", diff --git a/VERSION.txt b/VERSION.txt index bf21f52..8b707c6 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -0.6.6 \ No newline at end of file +0.6.7 \ No newline at end of file diff --git a/valu3/Cargo.toml b/valu3/Cargo.toml index c0fb830..cc79112 100644 --- a/valu3/Cargo.toml +++ b/valu3/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "valu3" -version = "0.6.6" +version = "0.6.7" edition = "2021" license = "Apache-2.0" readme = "crates-io.md" @@ -17,7 +17,7 @@ pest_derive = "2.7.15" regex = "1.11.1" chrono = "0.4.39" serde = { version = "1.0.216", features = ["derive"], optional = true } -valu3-derive = { path = "../valu3_derive", optional = true, version = "0.6.6" } +valu3-derive = { path = "../valu3_derive", optional = true, version = "0.6.7" } bincode = { version = "1.3.3", optional = true } [dev-dependencies] diff --git a/valu3/README.md b/valu3/README.md index cdb1e0d..52eb4bb 100644 --- a/valu3/README.md +++ b/valu3/README.md @@ -3,11 +3,11 @@ Welcome to **Valu3** - the ultimate, flexible, and powerful library for manipulating diverse data types in your Rust projects. Say goodbye to the complexity of handling numbers, strings, arrays, objects, and datetime values. Valu3 is here to make your life easier! -[![crates.io](https://img.shields.io/crates/v/valu3?label=0.6.6)](https://crates.io/crates/valu3) -[![Documentation](https://docs.rs/valu3/badge.svg?version=0.6.6)](https://docs.rs/valu3/0.6.6) +[![crates.io](https://img.shields.io/crates/v/valu3?label=0.6.7)](https://crates.io/crates/valu3) +[![Documentation](https://docs.rs/valu3/badge.svg?version=0.6.7)](https://docs.rs/valu3/0.6.7) ![MSRV](https://img.shields.io/badge/rustc-1.59+-ab6000.svg) ![Apache 2.0 licensed](https://img.shields.io/crates/l/actix-web.svg) -[![Dependency Status](https://deps.rs/crate/valu3/0.6.6/status.svg)](https://deps.rs/crate/valu3/0.6.6) +[![Dependency Status](https://deps.rs/crate/valu3/0.6.7/status.svg)](https://deps.rs/crate/valu3/0.6.7) ![Main test](https://github.com/lowcarboncode/valu3/actions/workflows/main-test.yml/badge.svg) [![codecov](https://codecov.io/gh/lowcarboncode/valu3/branch/master/graph/badge.svg)](https://codecov.io/gh/lowcarboncode/valu3) ![downloads](https://img.shields.io/crates/d/valu3.svg) diff --git a/valu3/src/serde_value/error.rs b/valu3/src/serde_value/error.rs deleted file mode 100644 index 7018dc6..0000000 --- a/valu3/src/serde_value/error.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std; -use std::fmt::{self, Display}; - -use serde::{de, ser}; - -pub type Result = std::result::Result; - -// This is a bare-bones implementation. A real library would provide additional -// information in its error type, for example the line and column at which the -// error occurred, the byte offset into the input, or the current key being -// processed. -#[derive(Debug)] -pub enum Error { - Message(String), -} - -impl ser::Error for Error { - fn custom(msg: T) -> Self { - Error::Message(msg.to_string()) - } -} - -impl de::Error for Error { - fn custom(msg: T) -> Self { - Error::Message(msg.to_string()) - } -} - -impl Display for Error { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::Message(msg) => formatter.write_str(msg), - } - } -} - -impl std::error::Error for Error {} diff --git a/valu3/src/serde_value/mod.rs b/valu3/src/serde_value/mod.rs index d319b72..a2a4f2a 100644 --- a/valu3/src/serde_value/mod.rs +++ b/valu3/src/serde_value/mod.rs @@ -1,2 +1,147 @@ pub mod de; pub mod ser; + +#[cfg(test)] +mod tests { + use crate::prelude::*; + use std::collections::HashMap; + + #[test] + fn test_serde_number() { + let value = Value::from(42u64); + let serialized = serde_json::to_string(&value).unwrap(); + assert_eq!(serialized, "42"); + + let deserialized: Value = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, value); + + let value = Value::from(3.14); + let serialized = serde_json::to_string(&value).unwrap(); + + let deserialized: Value = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, value); + + let value = Value::from(-3.14); + let serialized = serde_json::to_string(&value).unwrap(); + + let deserialized: Value = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, value); + + let value = Value::from(3.14e10); + let serialized = serde_json::to_string(&value).unwrap(); + + let deserialized: Value = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, value); + } + + #[test] + fn test_serde_string() { + let value = Value::from("hello"); + let serialized = serde_json::to_string(&value).unwrap(); + assert_eq!(serialized, "\"hello\""); + + let deserialized: Value = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, value); + } + + #[test] + fn test_serde_array() { + let value = Value::from(vec![ + Value::from(1u64), + Value::from(2u64), + Value::from(3u64), + ]); + let serialized = serde_json::to_string(&value).unwrap(); + assert_eq!(serialized, "[1,2,3]"); + + let deserialized: Value = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, value); + } + + #[test] + fn test_serde_object() { + let mut object = HashMap::new(); + object.insert("a", Value::from(1u64)); + object.insert("b", Value::from(2u64)); + object.insert("c", Value::from(3u64)); + let value = Value::from(object); + let serialized = serde_json::to_string(&value).unwrap(); + assert_eq!(serialized, r#"{"a":1,"b":2,"c":3}"#); + + let deserialized: Value = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, value); + } + + #[test] + fn test_serde_bool() { + let value = Value::from(true); + let serialized = serde_json::to_string(&value).unwrap(); + assert_eq!(serialized, "true"); + + let deserialized: Value = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, value); + } + + #[test] + fn test_serde_null() { + let value = Value::Null; + let serialized = serde_json::to_string(&value).unwrap(); + assert_eq!(serialized, "null"); + + let deserialized: Value = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, value); + } + + #[test] + fn test_serde_value() { + let value = Value::from(42u64); + let serialized = serde_json::to_string(&value).unwrap(); + assert_eq!(serialized, "42"); + + let deserialized: Value = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, value); + + let value = Value::from("hello"); + let serialized = serde_json::to_string(&value).unwrap(); + assert_eq!(serialized, "\"hello\""); + + let deserialized: Value = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, value); + + let value = Value::from(vec![ + Value::from(1u64), + Value::from(2u64), + Value::from(3u64), + ]); + let serialized = serde_json::to_string(&value).unwrap(); + assert_eq!(serialized, "[1,2,3]"); + + let deserialized: Value = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, value); + + let mut object = HashMap::new(); + object.insert("a", Value::from(1u64)); + object.insert("b", Value::from(2u64)); + object.insert("c", Value::from(3u64)); + let value = Value::from(object); + let serialized = serde_json::to_string(&value).unwrap(); + assert_eq!(serialized, r#"{"a":1,"b":2,"c":3}"#); + + let deserialized: Value = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, value); + + let value = Value::from(true); + let serialized = serde_json::to_string(&value).unwrap(); + assert_eq!(serialized, "true"); + + let deserialized: Value = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, value); + + let value = Value::Null; + let serialized = serde_json::to_string(&value).unwrap(); + assert_eq!(serialized, "null"); + + let deserialized: Value = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, value); + } +} diff --git a/valu3_derive/Cargo.toml b/valu3_derive/Cargo.toml index 12a62d2..66fe8a2 100644 --- a/valu3_derive/Cargo.toml +++ b/valu3_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "valu3-derive" -version = "0.6.6" +version = "0.6.7" edition = "2021" license = "Apache-2.0" readme = "crates-io.md" diff --git a/valu3_derive/README.md b/valu3_derive/README.md index cdb1e0d..52eb4bb 100644 --- a/valu3_derive/README.md +++ b/valu3_derive/README.md @@ -3,11 +3,11 @@ Welcome to **Valu3** - the ultimate, flexible, and powerful library for manipulating diverse data types in your Rust projects. Say goodbye to the complexity of handling numbers, strings, arrays, objects, and datetime values. Valu3 is here to make your life easier! -[![crates.io](https://img.shields.io/crates/v/valu3?label=0.6.6)](https://crates.io/crates/valu3) -[![Documentation](https://docs.rs/valu3/badge.svg?version=0.6.6)](https://docs.rs/valu3/0.6.6) +[![crates.io](https://img.shields.io/crates/v/valu3?label=0.6.7)](https://crates.io/crates/valu3) +[![Documentation](https://docs.rs/valu3/badge.svg?version=0.6.7)](https://docs.rs/valu3/0.6.7) ![MSRV](https://img.shields.io/badge/rustc-1.59+-ab6000.svg) ![Apache 2.0 licensed](https://img.shields.io/crates/l/actix-web.svg) -[![Dependency Status](https://deps.rs/crate/valu3/0.6.6/status.svg)](https://deps.rs/crate/valu3/0.6.6) +[![Dependency Status](https://deps.rs/crate/valu3/0.6.7/status.svg)](https://deps.rs/crate/valu3/0.6.7) ![Main test](https://github.com/lowcarboncode/valu3/actions/workflows/main-test.yml/badge.svg) [![codecov](https://codecov.io/gh/lowcarboncode/valu3/branch/master/graph/badge.svg)](https://codecov.io/gh/lowcarboncode/valu3) ![downloads](https://img.shields.io/crates/d/valu3.svg) From 752c462ee1c74297f107501551642ffd19408f0c Mon Sep 17 00:00:00 2001 From: Philippe Assis Date: Mon, 30 Dec 2024 09:20:20 -0300 Subject: [PATCH 2/5] Implement JSON parsing and conversion functionality; refactor tests for consistency --- .../src/parser/{value.pest => json/json.pest} | 0 valu3/src/parser/json/mod.rs | 159 +++++++++++++++ valu3/src/parser/mod.rs | 158 +-------------- valu3/src/parser/yaml/mod.rs | 185 ++++++++++++++++++ valu3/src/parser/yaml/yaml.pest | 35 ++++ valu3/src/to/json.rs | 6 +- 6 files changed, 384 insertions(+), 159 deletions(-) rename valu3/src/parser/{value.pest => json/json.pest} (100%) create mode 100644 valu3/src/parser/json/mod.rs create mode 100644 valu3/src/parser/yaml/mod.rs create mode 100644 valu3/src/parser/yaml/yaml.pest diff --git a/valu3/src/parser/value.pest b/valu3/src/parser/json/json.pest similarity index 100% rename from valu3/src/parser/value.pest rename to valu3/src/parser/json/json.pest diff --git a/valu3/src/parser/json/mod.rs b/valu3/src/parser/json/mod.rs new file mode 100644 index 0000000..8a1beb2 --- /dev/null +++ b/valu3/src/parser/json/mod.rs @@ -0,0 +1,159 @@ +use crate::prelude::*; +use pest::Parser; +use std::collections::HashMap; + +#[derive(Parser)] +#[grammar = "parser/json/json.pest"] +struct JSONParser; + +use pest::iterators::Pair; + +impl Value { + pub fn json_to_value(str: &str) -> Result { + let value = match JSONParser::parse(Rule::json, str.trim()) { + Ok(mut pairs) => match pairs.next() { + Some(pair) => Self::json_parse_value_inner(pair), + None => return Err(Error::NonParseble), + }, + Err(msg) => return Err(Error::NonParsebleMsg(msg.to_string())), + }; + Ok(value) + } + + fn json_parse_value_inner(pair: Pair) -> Self { + match pair.as_rule() { + Rule::object => { + let map = pair + .into_inner() + .map(|pair| { + let mut inner_rules = pair.into_inner(); + let name = inner_rules + .next() + .unwrap() + .into_inner() + .next() + .unwrap() + .as_str() + .to_string(); + let value = Self::json_parse_value_inner(inner_rules.next().unwrap()); + (name, value) + }) + .collect::>(); + + Self::from(map) + } + Rule::array => Self::from( + pair.into_inner() + .map(Self::json_parse_value_inner) + .collect::>(), + ), + Rule::string => Self::from(StringB::from(pair.into_inner().next().unwrap().as_str())), + Rule::number => Self::from(Number::try_from(pair.as_str()).unwrap()), + Rule::boolean => Self::Boolean(pair.as_str().parse().unwrap()), + Rule::null => Self::Null, + Rule::json + | Rule::EOI + | Rule::pair + | Rule::value + | Rule::inner + | Rule::char + | Rule::WHITESPACE => Self::Undefined, + } + } +} + +#[cfg(test)] +mod tests { + use crate::prelude::*; + use std::collections::HashMap; + + #[test] + fn json() { + let raw: &str = "{ + \"test\": true, + \"test2\": \"ok\", + \"test3\": [0, 1] + }"; + + let compare = Value::from({ + let mut map = HashMap::new(); + map.insert("test".to_string(), true.to_value()); + map.insert("test2".to_string(), "ok".to_value()); + map.insert("test3".to_string(), Value::from(vec![0, 1])); + map + }); + + assert_eq!(Value::json_to_value(raw), Ok(compare)); + } + + #[test] + fn array() { + let raw = "[0, true, null, \"ok\"]"; + + let compare = { + let mut list = Vec::new(); + list.push(Value::Number(Number::from(0))); + list.push(Value::Boolean(true)); + list.push(Value::Null); + list.push(Value::String(StringB::from("ok"))); + Value::from(list) + }; + + assert_eq!(Value::json_to_value(raw), Ok(compare)); + } + + #[test] + fn number() { + let int = "0"; + let float = "1.0"; + + assert_eq!( + Value::json_to_value(int), + Ok(Value::Number(Number::from(0))) + ); + assert_eq!( + Value::json_to_value(float), + Ok(Value::Number(Number::from(1.0))) + ); + } + + #[test] + fn string() { + let string = r#""string""#; + + assert_eq!( + Value::json_to_value(string), + Ok(Value::String(StringB::from("string"))) + ); + } + + #[test] + fn null() { + let null = "null"; + + assert_eq!(Value::json_to_value(null), Ok(Value::Null)); + } + #[test] + fn boolean() { + let boolean = "true"; + + assert_eq!(Value::json_to_value(boolean), Ok(Value::Boolean(true))); + } + + #[test] + fn all() { + let boolean = Value::json_to_value("true").unwrap(); + let float = Value::json_to_value("3.14").unwrap(); + let json = Value::json_to_value(r#"{"item": 3.14}"#).unwrap(); + let array = Value::json_to_value(r#"[1,2,3]"#).unwrap(); + let null = Value::json_to_value("null").unwrap(); + let string = Value::json_to_value(r#""123""#).unwrap(); + + assert_eq!(boolean, true.to_value()); + assert_eq!(float, 3.14.to_value()); + assert_eq!(json, Value::from(vec![("item", 3.14)])); + assert_eq!(array, vec![1, 2, 3].to_value()); + assert_eq!(null, Value::Null); + assert_eq!(string, "123".to_value()); + } +} diff --git a/valu3/src/parser/mod.rs b/valu3/src/parser/mod.rs index 40eda87..561a72a 100644 --- a/valu3/src/parser/mod.rs +++ b/valu3/src/parser/mod.rs @@ -1,156 +1,2 @@ -use crate::prelude::*; -use pest::Parser; -use std::collections::HashMap; - -#[derive(Parser)] -#[grammar = "parser/value.pest"] -struct JSONParser; - -use pest::iterators::Pair; - -impl Value { - pub fn payload_to_value(str: &str) -> Result { - let value = match JSONParser::parse(Rule::json, str.trim()) { - Ok(mut pairs) => match pairs.next() { - Some(pair) => Self::parse_value(pair), - None => return Err(Error::NonParseble), - }, - Err(msg) => return Err(Error::NonParsebleMsg(msg.to_string())), - }; - Ok(value) - } - - /// Parses a `Pair` from `pest` to a `Value`. - fn parse_value(pair: Pair) -> Self { - match pair.as_rule() { - Rule::object => { - let map = pair - .into_inner() - .map(|pair| { - let mut inner_rules = pair.into_inner(); - let name = inner_rules - .next() - .unwrap() - .into_inner() - .next() - .unwrap() - .as_str() - .to_string(); - let value = Self::parse_value(inner_rules.next().unwrap()); - (name, value) - }) - .collect::>(); - - Self::from(map) - } - Rule::array => Self::from(pair.into_inner().map(Self::parse_value).collect::>()), - Rule::string => Self::from(StringB::from(pair.into_inner().next().unwrap().as_str())), - Rule::number => Self::from(Number::try_from(pair.as_str()).unwrap()), - Rule::boolean => Self::Boolean(pair.as_str().parse().unwrap()), - Rule::null => Self::Null, - Rule::json - | Rule::EOI - | Rule::pair - | Rule::value - | Rule::inner - | Rule::char - | Rule::WHITESPACE => Self::Undefined, - } - } -} - -#[cfg(test)] -mod tests { - use crate::prelude::*; - use std::collections::HashMap; - - #[test] - fn json() { - let raw: &str = "{ - \"test\": true, - \"test2\": \"ok\", - \"test3\": [0, 1] - }"; - - let compare = Value::from({ - let mut map = HashMap::new(); - map.insert("test".to_string(), true.to_value()); - map.insert("test2".to_string(), "ok".to_value()); - map.insert("test3".to_string(), Value::from(vec![0, 1])); - map - }); - - assert_eq!(Value::payload_to_value(raw), Ok(compare)); - } - - #[test] - fn array() { - let raw = "[0, true, null, \"ok\"]"; - - let compare = { - let mut list = Vec::new(); - list.push(Value::Number(Number::from(0))); - list.push(Value::Boolean(true)); - list.push(Value::Null); - list.push(Value::String(StringB::from("ok"))); - Value::from(list) - }; - - assert_eq!(Value::payload_to_value(raw), Ok(compare)); - } - - #[test] - fn number() { - let int = "0"; - let float = "1.0"; - - assert_eq!( - Value::payload_to_value(int), - Ok(Value::Number(Number::from(0))) - ); - assert_eq!( - Value::payload_to_value(float), - Ok(Value::Number(Number::from(1.0))) - ); - } - - #[test] - fn string() { - let string = r#""string""#; - - assert_eq!( - Value::payload_to_value(string), - Ok(Value::String(StringB::from("string"))) - ); - } - - #[test] - fn null() { - let null = "null"; - - assert_eq!(Value::payload_to_value(null), Ok(Value::Null)); - } - #[test] - fn boolean() { - let boolean = "true"; - - assert_eq!(Value::payload_to_value(boolean), Ok(Value::Boolean(true))); - } - - #[test] - fn all() { - let boolean = Value::payload_to_value("true").unwrap(); - let float = Value::payload_to_value("3.14").unwrap(); - let json = Value::payload_to_value(r#"{"item": 3.14}"#).unwrap(); - let array = Value::payload_to_value(r#"[1,2,3]"#).unwrap(); - let null = Value::payload_to_value("null").unwrap(); - let string = Value::payload_to_value(r#""123""#).unwrap(); - - assert_eq!(boolean, true.to_value()); - assert_eq!(float, 3.14.to_value()); - assert_eq!(json, Value::from(vec![("item", 3.14)])); - assert_eq!(array, vec![1, 2, 3].to_value()); - assert_eq!(null, Value::Null); - assert_eq!(string, "123".to_value()); - } -} +pub mod json; +pub mod yaml; diff --git a/valu3/src/parser/yaml/mod.rs b/valu3/src/parser/yaml/mod.rs new file mode 100644 index 0000000..5d57297 --- /dev/null +++ b/valu3/src/parser/yaml/mod.rs @@ -0,0 +1,185 @@ +use crate::prelude::*; +use pest::Parser; +use std::collections::HashMap; + +#[derive(Parser)] +#[grammar = "parser/yaml/yaml.pest"] // Certifique-se de que a gramática YAML está nesse caminho +struct YAMLParser; + +use pest::iterators::Pair; + +impl Value { + pub fn yaml_to_value(str: &str) -> Result { + let value = match YAMLParser::parse(Rule::yaml, str.trim()) { + Ok(mut pairs) => match pairs.next() { + Some(pair) => Self::yaml_parse_value_inner(pair), + None => return Err(Error::NonParseble), + }, + Err(msg) => return Err(Error::NonParsebleMsg(msg.to_string())), + }; + Ok(value) + } + + fn yaml_parse_value_inner(pair: Pair) -> Self { + match pair.as_rule() { + Rule::document => { + // A regra document pode ser uma lista de key_value_pair ou list_item + Self::from( + pair.into_inner() + .map(Self::yaml_parse_value_inner) + .collect::>(), + ) + } + Rule::object => { + let map = pair + .into_inner() + .map(|pair| { + let mut inner_rules = pair.into_inner(); + let name = inner_rules + .next() + .unwrap() + .into_inner() + .next() + .unwrap() + .as_str() + .to_string(); + let value = Self::yaml_parse_value_inner(inner_rules.next().unwrap()); + (name, value) + }) + .collect::>(); + + Self::from(map) + } + Rule::array => Self::from( + pair.into_inner() + .map(Self::yaml_parse_value_inner) + .collect::>(), + ), + Rule::string => Self::from(StringB::from(pair.into_inner().next().unwrap().as_str())), + Rule::number => Self::from(Number::try_from(pair.as_str()).unwrap()), + Rule::boolean => Self::Boolean(pair.as_str().parse().unwrap()), + Rule::null => Self::Null, + Rule::key => { + // A chave deve ser uma string + Self::from(pair.as_str().to_string()) + } + Rule::inner_string => { + // A string interna, sem as aspas + Self::from(pair.as_str().to_string()) + } + Rule::key_value_pair | Rule::list_item | Rule::value | Rule::EOI | Rule::yaml => { + Self::Undefined + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::prelude::*; + use std::collections::HashMap; + + #[test] + fn yaml() { + let raw: &str = " + test: true + test2: \"ok\" + test3: + - 0 + - 1 + "; + + let compare = Value::from({ + let mut map = HashMap::new(); + map.insert("test".to_string(), true.to_value()); + map.insert("test2".to_string(), "ok".to_value()); + map.insert( + "test3".to_string(), + Value::from(vec![ + Value::Number(Number::from(0)), + Value::Number(Number::from(1)), + ]), + ); + map + }); + + assert_eq!(Value::yaml_to_value(raw), Ok(compare)); + } + + #[test] + fn array() { + let raw = " + - 0 + - true + - null + - \"ok\" + "; + + let compare = { + let mut list = Vec::new(); + list.push(Value::Number(Number::from(0))); + list.push(Value::Boolean(true)); + list.push(Value::Null); + list.push(Value::String(StringB::from("ok"))); + Value::from(list) + }; + + assert_eq!(Value::yaml_to_value(raw), Ok(compare)); + } + + #[test] + fn number() { + let int = "0"; + let float = "1.0"; + + assert_eq!( + Value::yaml_to_value(int), + Ok(Value::Number(Number::from(0))) + ); + assert_eq!( + Value::yaml_to_value(float), + Ok(Value::Number(Number::from(1.0))) + ); + } + + #[test] + fn string() { + let string = r#""string""#; + + assert_eq!( + Value::yaml_to_value(string), + Ok(Value::String(StringB::from("string"))) + ); + } + + #[test] + fn null() { + let null = "null"; + + assert_eq!(Value::yaml_to_value(null), Ok(Value::Null)); + } + + #[test] + fn boolean() { + let boolean = "true"; + + assert_eq!(Value::yaml_to_value(boolean), Ok(Value::Boolean(true))); + } + + #[test] + fn all() { + let boolean = Value::yaml_to_value("true").unwrap(); + let float = Value::yaml_to_value("3.14").unwrap(); + let yaml = Value::yaml_to_value(r#"test: 3.14"#).unwrap(); + let array = Value::yaml_to_value(r#"- 1\n- 2\n- 3"#).unwrap(); + let null = Value::yaml_to_value("null").unwrap(); + let string = Value::yaml_to_value(r#""123""#).unwrap(); + + assert_eq!(boolean, true.to_value()); + assert_eq!(float, 3.14.to_value()); + assert_eq!(yaml, Value::from(vec![("test", 3.14)])); + assert_eq!(array, vec![1, 2, 3].to_value()); + assert_eq!(null, Value::Null); + assert_eq!(string, "123".to_value()); + } +} diff --git a/valu3/src/parser/yaml/yaml.pest b/valu3/src/parser/yaml/yaml.pest new file mode 100644 index 0000000..bfe93ec --- /dev/null +++ b/valu3/src/parser/yaml/yaml.pest @@ -0,0 +1,35 @@ +yaml = { SOI ~ (document)* ~ EOI } + +WHITESPACE = _{ " " | "\t" | "\r" | "\n" } + +document = { (key_value_pair | list_item)+ } + +key_value_pair = { key ~ ":" ~ value } + +key = { char+ } + +value = _{ string | number | boolean | null | object | array | block_value } + +string = ${ "\"" ~ inner ~ "\"" } +inner = @{ char* } +char = { + !("\"" | "\\") ~ ANY + | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") + | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4}) +} + +number = @{ ("-"? ~ ("0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*) ~ ("." ~ ASCII_DIGIT*)? ~ ("e" ~ ("+" | "-")? ~ ASCII_DIGIT+)?) } + +boolean = { "true" | "false" } + +null = { "null" } + +object = { "{" ~ (key_value_pair ~ ("," ~ key_value_pair)*)? ~ "}" } + +array = { "[" ~ (value ~ ("," ~ value)*)? ~ "]" } + +list_item = { "-" ~ " " ~ value } + +block_value = { indent_block ~ value } + +indent_block = { " "+ } diff --git a/valu3/src/to/json.rs b/valu3/src/to/json.rs index e78e301..90ce3fe 100644 --- a/valu3/src/to/json.rs +++ b/valu3/src/to/json.rs @@ -132,9 +132,9 @@ mod tests { #[test] fn it_should_convert_a_value_to_json_string() { - let value_str = Value::payload_to_value("{\"name\":\"John Doe\"}").unwrap(); - let value_number = Value::payload_to_value("{\"age\":30}").unwrap(); - let value_boolean = Value::payload_to_value("{\"is_active\":true}").unwrap(); + let value_str = Value::json_to_value("{\"name\":\"John Doe\"}").unwrap(); + let value_number = Value::json_to_value("{\"age\":30}").unwrap(); + let value_boolean = Value::json_to_value("{\"is_active\":true}").unwrap(); assert_eq!( "{\n\t\"name\": \"John Doe\"\n}", value_str.to_json(JsonMode::Indented) From a24e1147fde2e1f55897055bbb188c3aace15649 Mon Sep 17 00:00:00 2001 From: Philippe Assis Date: Mon, 30 Dec 2024 14:16:49 -0300 Subject: [PATCH 3/5] Update Cargo.lock and Cargo.toml; add pest-cli and update JSON/YAML parsers --- Cargo.lock | 149 ++++++++++++++++++++++++++++++++ Cargo.toml | 5 +- example.json | 22 +++++ valu3/src/parser/json/json.pest | 41 ++++----- valu3/src/parser/json/mod.rs | 2 +- valu3/src/parser/yaml/yaml.pest | 62 ++++++------- 6 files changed, 224 insertions(+), 57 deletions(-) create mode 100644 example.json diff --git a/Cargo.lock b/Cargo.lock index 7ecab7a..6b6ce41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,55 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -82,6 +131,52 @@ dependencies = [ "windows-targets 0.52.0", ] +[[package]] +name = "clap" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + [[package]] name = "core-foundation-sys" version = "0.8.4" @@ -138,6 +233,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "iana-time-zone" version = "0.1.57" @@ -161,6 +262,12 @@ dependencies = [ "cc", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itoa" version = "1.0.10" @@ -220,6 +327,17 @@ dependencies = [ "ucd-trie", ] +[[package]] +name = "pest-cli" +version = "0.1.0" +dependencies = [ + "clap", + "pest", + "pest_derive", + "pest_meta", + "pest_vm", +] + [[package]] name = "pest_derive" version = "2.7.15" @@ -254,6 +372,16 @@ dependencies = [ "sha2", ] +[[package]] +name = "pest_vm" +version = "2.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8151168a80801131f6e0e79d6c84fa337ccd2493c99e59de027354c3e6fca0b" +dependencies = [ + "pest", + "pest_meta", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -386,6 +514,12 @@ dependencies = [ "digest", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.91" @@ -435,6 +569,12 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "valu3" version = "0.6.7" @@ -534,6 +674,15 @@ dependencies = [ "windows-targets 0.48.1", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-targets" version = "0.48.1" diff --git a/Cargo.toml b/Cargo.toml index 0b3e68a..eb1ebf7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,3 @@ [workspace] -members = [ - "valu3", - "valu3_derive", -] +members = ["valu3", "valu3_derive", "pest-cli"] resolver = "2" diff --git a/example.json b/example.json new file mode 100644 index 0000000..0573ed2 --- /dev/null +++ b/example.json @@ -0,0 +1,22 @@ +{ + "menu": { + "id": "file", + "value": "File", + "popup": { + "menuitem": [ + { + "value": "New", + "onclick": "CreateNewDoc()" + }, + { + "value": "Open", + "onclick": "OpenDoc()" + }, + { + "value": "Close", + "onclick": "CloseDoc()" + } + ] + } + } +} \ No newline at end of file diff --git a/valu3/src/parser/json/json.pest b/valu3/src/parser/json/json.pest index e613c04..b7ced78 100644 --- a/valu3/src/parser/json/json.pest +++ b/valu3/src/parser/json/json.pest @@ -1,27 +1,24 @@ -json = _{ SOI ~ (object | array | string | number | boolean | null) ~ EOI } -WHITESPACE = _{ " " | "\t" | "\r" | "\n" } -object = { - ("#{" | "{") ~ "}" | - ("#{" | "{") ~ pair ~ ("," ~ pair)* ~ "}" +json = _{ SOI ~ (object | array | string | number | boolean | null) ~ EOI } +WHITESPACE = _{ " " | "\t" | "\r" | "\n" } +object = { + ("#{" | "{") ~ "}" + | ("#{" | "{") ~ key_value_pair ~ ("," ~ key_value_pair)* ~ "}" } -pair = { string ~ ":" ~ value } -array = { - "[" ~ "]" | - "[" ~ value ~ ("," ~ value)* ~ "]" +key_value_pair = { string ~ ":" ~ value } +array = { + "[" ~ "]" + | "[" ~ value ~ ("," ~ value)* ~ "]" } -value = _{ object | array | string | number | boolean | null } -boolean = { "true" | "false" } -null = { "null" } -string = ${ "\"" ~ inner ~ "\"" } -inner = @{ char* } -char = { +value = _{ object | array | string | number | boolean | null } +boolean = { "true" | "false" } +null = { "null" } +string = ${ "\"" ~ inner ~ "\"" } +inner = @{ char* } +char = { !("\"" | "\\") ~ ANY - | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") - | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4}) + | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") + | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4}) } -number = @{ - "-"? - ~ ("0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*) - ~ ("." ~ ASCII_DIGIT*)? - ~ (^"e" ~ ("+" | "-")? ~ ASCII_DIGIT+)? +number = @{ + "-"? ~ ("0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*) ~ ("." ~ ASCII_DIGIT*)? ~ (^"e" ~ ("+" | "-")? ~ ASCII_DIGIT+)? } diff --git a/valu3/src/parser/json/mod.rs b/valu3/src/parser/json/mod.rs index 8a1beb2..fccab32 100644 --- a/valu3/src/parser/json/mod.rs +++ b/valu3/src/parser/json/mod.rs @@ -53,7 +53,7 @@ impl Value { Rule::null => Self::Null, Rule::json | Rule::EOI - | Rule::pair + | Rule::key_value_pair | Rule::value | Rule::inner | Rule::char diff --git a/valu3/src/parser/yaml/yaml.pest b/valu3/src/parser/yaml/yaml.pest index bfe93ec..16ce4d1 100644 --- a/valu3/src/parser/yaml/yaml.pest +++ b/valu3/src/parser/yaml/yaml.pest @@ -1,35 +1,37 @@ -yaml = { SOI ~ (document)* ~ EOI } +yaml = _{ SOI ~ key_value_pair* ~ EOI } +BREAK_LINE = _{ "\n\r" | "\n" | "\r" } -WHITESPACE = _{ " " | "\t" | "\r" | "\n" } - -document = { (key_value_pair | list_item)+ } - -key_value_pair = { key ~ ":" ~ value } - -key = { char+ } +key_value_pair = { key ~ ":" ~ value } +key = @{ (ASCII_ALPHANUMERIC | "_") ~ (ASCII_ALPHANUMERIC | "_" | "-")* } +list_item = _{ "-" ~ value } -value = _{ string | number | boolean | null | object | array | block_value } +value = _{ number | boolean | null | list | map | string } +WHITESPACE = _{ " " | "\t" | "\r" | "\n" } -string = ${ "\"" ~ inner ~ "\"" } -inner = @{ char* } -char = { - !("\"" | "\\") ~ ANY - | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") +string_single_quote = _{ "'" ~ ident ~ "'" } +string_double_quote = _{ "\"" ~ ident ~ "\"" } +string_unescaped = _{ ident_unescaped ~ BREAK_LINE? } +string = { string_single_quote | string_double_quote | string_unescaped } +ident = @{ char* } +char = { + !("\"" | "'" | "\\") ~ ANY + | "\\" ~ ("\"" | "'" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4}) } - -number = @{ ("-"? ~ ("0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*) ~ ("." ~ ASCII_DIGIT*)? ~ ("e" ~ ("+" | "-")? ~ ASCII_DIGIT+)?) } - -boolean = { "true" | "false" } - -null = { "null" } - -object = { "{" ~ (key_value_pair ~ ("," ~ key_value_pair)*)? ~ "}" } - -array = { "[" ~ (value ~ ("," ~ value)*)? ~ "]" } - -list_item = { "-" ~ " " ~ value } - -block_value = { indent_block ~ value } - -indent_block = { " "+ } +ident_unescaped = @{ char_unescaped* } +char_unescaped = { !("\n" | "\r") ~ ANY } + +null = { "null" } +undefined = { "undefined" } +boolean = { "true" | "false" } +number = @{ + "-"? ~ ("0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*) ~ ("." ~ ASCII_DIGIT*)? ~ (^"e" ~ ("+" | "-")? ~ ASCII_DIGIT+)? +} +list = { "[" ~ (value ~ ("," ~ value)*)? ~ "]" } + +// map +map = { "{" ~ (key_value_pair_map ~ ("," ~ key_value_pair_map)*)? ~ "}" } +key_value_pair_map = { map_key ~ ":" ~ map_value } +map_key = { string_double_quote } +map_value = { number | boolean | null | map_list | map | string_double_quote | undefined } +map_list = { "[" ~ (map_value ~ ("," ~ map_value)*)? ~ "]" } From 1fd945035f29cc48ab5b52718cfa646246278f98 Mon Sep 17 00:00:00 2001 From: Philippe Assis Date: Mon, 30 Dec 2024 15:01:05 -0300 Subject: [PATCH 4/5] Remove YAML parser module and related dependencies; update JSON serialization tests for flexibility --- Cargo.lock | 149 ------------------------- Cargo.toml | 2 +- valu3/src/parser/mod.rs | 1 - valu3/src/parser/yaml/mod.rs | 185 -------------------------------- valu3/src/parser/yaml/yaml.pest | 37 ------- valu3/src/serde_value/mod.rs | 20 +++- 6 files changed, 19 insertions(+), 375 deletions(-) delete mode 100644 valu3/src/parser/yaml/mod.rs delete mode 100644 valu3/src/parser/yaml/yaml.pest diff --git a/Cargo.lock b/Cargo.lock index 6b6ce41..7ecab7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,55 +26,6 @@ dependencies = [ "libc", ] -[[package]] -name = "anstream" -version = "0.6.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" - -[[package]] -name = "anstyle-parse" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" -dependencies = [ - "anstyle", - "windows-sys", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -131,52 +82,6 @@ dependencies = [ "windows-targets 0.52.0", ] -[[package]] -name = "clap" -version = "4.5.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" - -[[package]] -name = "colorchoice" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" - [[package]] name = "core-foundation-sys" version = "0.8.4" @@ -233,12 +138,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - [[package]] name = "iana-time-zone" version = "0.1.57" @@ -262,12 +161,6 @@ dependencies = [ "cc", ] -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - [[package]] name = "itoa" version = "1.0.10" @@ -327,17 +220,6 @@ dependencies = [ "ucd-trie", ] -[[package]] -name = "pest-cli" -version = "0.1.0" -dependencies = [ - "clap", - "pest", - "pest_derive", - "pest_meta", - "pest_vm", -] - [[package]] name = "pest_derive" version = "2.7.15" @@ -372,16 +254,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "pest_vm" -version = "2.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8151168a80801131f6e0e79d6c84fa337ccd2493c99e59de027354c3e6fca0b" -dependencies = [ - "pest", - "pest_meta", -] - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -514,12 +386,6 @@ dependencies = [ "digest", ] -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - [[package]] name = "syn" version = "2.0.91" @@ -569,12 +435,6 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - [[package]] name = "valu3" version = "0.6.7" @@ -674,15 +534,6 @@ dependencies = [ "windows-targets 0.48.1", ] -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.0", -] - [[package]] name = "windows-targets" version = "0.48.1" diff --git a/Cargo.toml b/Cargo.toml index eb1ebf7..b08be80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["valu3", "valu3_derive", "pest-cli"] +members = ["valu3", "valu3_derive"] resolver = "2" diff --git a/valu3/src/parser/mod.rs b/valu3/src/parser/mod.rs index 561a72a..22fdbb3 100644 --- a/valu3/src/parser/mod.rs +++ b/valu3/src/parser/mod.rs @@ -1,2 +1 @@ pub mod json; -pub mod yaml; diff --git a/valu3/src/parser/yaml/mod.rs b/valu3/src/parser/yaml/mod.rs deleted file mode 100644 index 5d57297..0000000 --- a/valu3/src/parser/yaml/mod.rs +++ /dev/null @@ -1,185 +0,0 @@ -use crate::prelude::*; -use pest::Parser; -use std::collections::HashMap; - -#[derive(Parser)] -#[grammar = "parser/yaml/yaml.pest"] // Certifique-se de que a gramática YAML está nesse caminho -struct YAMLParser; - -use pest::iterators::Pair; - -impl Value { - pub fn yaml_to_value(str: &str) -> Result { - let value = match YAMLParser::parse(Rule::yaml, str.trim()) { - Ok(mut pairs) => match pairs.next() { - Some(pair) => Self::yaml_parse_value_inner(pair), - None => return Err(Error::NonParseble), - }, - Err(msg) => return Err(Error::NonParsebleMsg(msg.to_string())), - }; - Ok(value) - } - - fn yaml_parse_value_inner(pair: Pair) -> Self { - match pair.as_rule() { - Rule::document => { - // A regra document pode ser uma lista de key_value_pair ou list_item - Self::from( - pair.into_inner() - .map(Self::yaml_parse_value_inner) - .collect::>(), - ) - } - Rule::object => { - let map = pair - .into_inner() - .map(|pair| { - let mut inner_rules = pair.into_inner(); - let name = inner_rules - .next() - .unwrap() - .into_inner() - .next() - .unwrap() - .as_str() - .to_string(); - let value = Self::yaml_parse_value_inner(inner_rules.next().unwrap()); - (name, value) - }) - .collect::>(); - - Self::from(map) - } - Rule::array => Self::from( - pair.into_inner() - .map(Self::yaml_parse_value_inner) - .collect::>(), - ), - Rule::string => Self::from(StringB::from(pair.into_inner().next().unwrap().as_str())), - Rule::number => Self::from(Number::try_from(pair.as_str()).unwrap()), - Rule::boolean => Self::Boolean(pair.as_str().parse().unwrap()), - Rule::null => Self::Null, - Rule::key => { - // A chave deve ser uma string - Self::from(pair.as_str().to_string()) - } - Rule::inner_string => { - // A string interna, sem as aspas - Self::from(pair.as_str().to_string()) - } - Rule::key_value_pair | Rule::list_item | Rule::value | Rule::EOI | Rule::yaml => { - Self::Undefined - } - } - } -} - -#[cfg(test)] -mod tests { - use crate::prelude::*; - use std::collections::HashMap; - - #[test] - fn yaml() { - let raw: &str = " - test: true - test2: \"ok\" - test3: - - 0 - - 1 - "; - - let compare = Value::from({ - let mut map = HashMap::new(); - map.insert("test".to_string(), true.to_value()); - map.insert("test2".to_string(), "ok".to_value()); - map.insert( - "test3".to_string(), - Value::from(vec![ - Value::Number(Number::from(0)), - Value::Number(Number::from(1)), - ]), - ); - map - }); - - assert_eq!(Value::yaml_to_value(raw), Ok(compare)); - } - - #[test] - fn array() { - let raw = " - - 0 - - true - - null - - \"ok\" - "; - - let compare = { - let mut list = Vec::new(); - list.push(Value::Number(Number::from(0))); - list.push(Value::Boolean(true)); - list.push(Value::Null); - list.push(Value::String(StringB::from("ok"))); - Value::from(list) - }; - - assert_eq!(Value::yaml_to_value(raw), Ok(compare)); - } - - #[test] - fn number() { - let int = "0"; - let float = "1.0"; - - assert_eq!( - Value::yaml_to_value(int), - Ok(Value::Number(Number::from(0))) - ); - assert_eq!( - Value::yaml_to_value(float), - Ok(Value::Number(Number::from(1.0))) - ); - } - - #[test] - fn string() { - let string = r#""string""#; - - assert_eq!( - Value::yaml_to_value(string), - Ok(Value::String(StringB::from("string"))) - ); - } - - #[test] - fn null() { - let null = "null"; - - assert_eq!(Value::yaml_to_value(null), Ok(Value::Null)); - } - - #[test] - fn boolean() { - let boolean = "true"; - - assert_eq!(Value::yaml_to_value(boolean), Ok(Value::Boolean(true))); - } - - #[test] - fn all() { - let boolean = Value::yaml_to_value("true").unwrap(); - let float = Value::yaml_to_value("3.14").unwrap(); - let yaml = Value::yaml_to_value(r#"test: 3.14"#).unwrap(); - let array = Value::yaml_to_value(r#"- 1\n- 2\n- 3"#).unwrap(); - let null = Value::yaml_to_value("null").unwrap(); - let string = Value::yaml_to_value(r#""123""#).unwrap(); - - assert_eq!(boolean, true.to_value()); - assert_eq!(float, 3.14.to_value()); - assert_eq!(yaml, Value::from(vec![("test", 3.14)])); - assert_eq!(array, vec![1, 2, 3].to_value()); - assert_eq!(null, Value::Null); - assert_eq!(string, "123".to_value()); - } -} diff --git a/valu3/src/parser/yaml/yaml.pest b/valu3/src/parser/yaml/yaml.pest deleted file mode 100644 index 16ce4d1..0000000 --- a/valu3/src/parser/yaml/yaml.pest +++ /dev/null @@ -1,37 +0,0 @@ -yaml = _{ SOI ~ key_value_pair* ~ EOI } -BREAK_LINE = _{ "\n\r" | "\n" | "\r" } - -key_value_pair = { key ~ ":" ~ value } -key = @{ (ASCII_ALPHANUMERIC | "_") ~ (ASCII_ALPHANUMERIC | "_" | "-")* } -list_item = _{ "-" ~ value } - -value = _{ number | boolean | null | list | map | string } -WHITESPACE = _{ " " | "\t" | "\r" | "\n" } - -string_single_quote = _{ "'" ~ ident ~ "'" } -string_double_quote = _{ "\"" ~ ident ~ "\"" } -string_unescaped = _{ ident_unescaped ~ BREAK_LINE? } -string = { string_single_quote | string_double_quote | string_unescaped } -ident = @{ char* } -char = { - !("\"" | "'" | "\\") ~ ANY - | "\\" ~ ("\"" | "'" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") - | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4}) -} -ident_unescaped = @{ char_unescaped* } -char_unescaped = { !("\n" | "\r") ~ ANY } - -null = { "null" } -undefined = { "undefined" } -boolean = { "true" | "false" } -number = @{ - "-"? ~ ("0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*) ~ ("." ~ ASCII_DIGIT*)? ~ (^"e" ~ ("+" | "-")? ~ ASCII_DIGIT+)? -} -list = { "[" ~ (value ~ ("," ~ value)*)? ~ "]" } - -// map -map = { "{" ~ (key_value_pair_map ~ ("," ~ key_value_pair_map)*)? ~ "}" } -key_value_pair_map = { map_key ~ ":" ~ map_value } -map_key = { string_double_quote } -map_value = { number | boolean | null | map_list | map | string_double_quote | undefined } -map_list = { "[" ~ (map_value ~ ("," ~ map_value)*)? ~ "]" } diff --git a/valu3/src/serde_value/mod.rs b/valu3/src/serde_value/mod.rs index a2a4f2a..ebfe071 100644 --- a/valu3/src/serde_value/mod.rs +++ b/valu3/src/serde_value/mod.rs @@ -66,7 +66,15 @@ mod tests { object.insert("c", Value::from(3u64)); let value = Value::from(object); let serialized = serde_json::to_string(&value).unwrap(); - assert_eq!(serialized, r#"{"a":1,"b":2,"c":3}"#); + + let cases = [ + r#"{"a":1,"b":2,"c":3}"#, + r#"{"b":2,"a":1,"c":3}"#, + r#"{"b":2,"c":3,"a":1}"#, + r#"{"c":3,"b":2,"a":1}"#, + r#"{"c":3,"a":1,"b":2}"#, + ]; + assert_eq!(cases.contains(&serialized.as_str()), true); let deserialized: Value = serde_json::from_str(&serialized).unwrap(); assert_eq!(deserialized, value); @@ -125,7 +133,15 @@ mod tests { object.insert("c", Value::from(3u64)); let value = Value::from(object); let serialized = serde_json::to_string(&value).unwrap(); - assert_eq!(serialized, r#"{"a":1,"b":2,"c":3}"#); + let cases = [ + r#"{"a":1,"b":2,"c":3}"#, + r#"{"b":2,"a":1,"c":3}"#, + r#"{"b":2,"c":3,"a":1}"#, + r#"{"c":3,"b":2,"a":1}"#, + r#"{"c":3,"a":1,"b":2}"#, + ]; + println!("{}", serialized); + assert_eq!(cases.contains(&serialized.as_str()), true); let deserialized: Value = serde_json::from_str(&serialized).unwrap(); assert_eq!(deserialized, value); From 79e47f88f545fa1b49e47132ce64a9c73a560f85 Mon Sep 17 00:00:00 2001 From: Philippe Assis Date: Mon, 30 Dec 2024 15:04:49 -0300 Subject: [PATCH 5/5] Add additional test cases for JSON serialization order in serde_value module --- valu3/src/serde_value/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/valu3/src/serde_value/mod.rs b/valu3/src/serde_value/mod.rs index ebfe071..aa85777 100644 --- a/valu3/src/serde_value/mod.rs +++ b/valu3/src/serde_value/mod.rs @@ -69,6 +69,7 @@ mod tests { let cases = [ r#"{"a":1,"b":2,"c":3}"#, + r#"{"a":1,"c":3,"b":2}"#, r#"{"b":2,"a":1,"c":3}"#, r#"{"b":2,"c":3,"a":1}"#, r#"{"c":3,"b":2,"a":1}"#, @@ -135,12 +136,12 @@ mod tests { let serialized = serde_json::to_string(&value).unwrap(); let cases = [ r#"{"a":1,"b":2,"c":3}"#, + r#"{"a":1,"c":3,"b":2}"#, r#"{"b":2,"a":1,"c":3}"#, r#"{"b":2,"c":3,"a":1}"#, r#"{"c":3,"b":2,"a":1}"#, r#"{"c":3,"a":1,"b":2}"#, ]; - println!("{}", serialized); assert_eq!(cases.contains(&serialized.as_str()), true); let deserialized: Value = serde_json::from_str(&serialized).unwrap();