diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 882f637..9ab28f4 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -34,6 +34,8 @@ jobs: run: cargo test --example edn_to_json --features "json" - name: example_async run: cargo run --example async --features "async" + - name: example_no_sets + run: cargo run --example struct_from_str --no-default-features fmt: runs-on: ubuntu-latest diff --git a/src/deserialize/mod.rs b/src/deserialize/mod.rs index 8b32dd8..6d624db 100644 --- a/src/deserialize/mod.rs +++ b/src/deserialize/mod.rs @@ -5,6 +5,7 @@ use std::str::FromStr; pub mod parse; +#[cfg(feature = "sets")] use ordered_float::OrderedFloat; /// public trait to be used to `Deserialize` structs. @@ -73,6 +74,7 @@ impl Deserialize for () { } } +#[cfg(feature = "sets")] impl Deserialize for OrderedFloat { fn deserialize(edn: &Edn) -> Result { edn.to_float() @@ -168,6 +170,7 @@ where .ok_or_else(|| Error::Iter(format!("Could not create iter from {edn:?}")))? .map(|e| Deserialize::deserialize(e)) .collect::>()?), + #[cfg(feature = "sets")] Edn::Set(_) => Ok(edn .iter_some() .ok_or_else(|| Error::Iter(format!("Could not create iter from {edn:?}")))? @@ -267,6 +270,7 @@ where } } +#[cfg(feature = "sets")] impl Deserialize for HashSet where T: std::cmp::Eq + std::hash::Hash + Deserialize, @@ -291,6 +295,7 @@ where } } +#[cfg(feature = "sets")] impl Deserialize for BTreeSet where T: std::cmp::Eq + std::hash::Hash + std::cmp::Ord + Deserialize, @@ -440,7 +445,9 @@ pub fn from_edn(edn: &Edn) -> Result { #[cfg(test)] mod test { use super::*; - use crate::edn::{List, Map, Set, Vector}; + #[cfg(feature = "sets")] + use crate::edn::Set; + use crate::edn::{List, Map, Vector}; use crate::{hmap, hset, map, set}; #[test] @@ -452,6 +459,7 @@ mod test { } #[test] + #[cfg(feature = "sets")] fn deser_btreeset_with_error() { let edn = "#{\"a\", 5, \"b\"}"; let err: Result, Error> = from_str(edn); @@ -498,6 +506,7 @@ mod test { } #[test] + #[cfg(feature = "sets")] fn from_str_list_with_set() { let edn = "(1 -10 \"2\" 3.3 :b #{true \\c})"; @@ -528,6 +537,7 @@ mod test { } #[test] + #[cfg(feature = "sets")] fn from_str_complex_map() { let edn = "{:a \"2\" :b [true false] :c #{:A {:a :b} nil}}"; @@ -633,6 +643,7 @@ mod test { } #[test] + #[cfg(feature = "sets")] fn test_more_sym() { let edn: Edn = Edn::from_str("(a \\b \"c\" 5 #{hello world})").unwrap(); let expected = Edn::List(List::new(vec![ @@ -722,6 +733,7 @@ mod test { } #[test] + #[cfg(feature = "sets")] fn deser_btreeset() { let set = Edn::Set(Set::new(set! { Edn::UInt(4), @@ -737,6 +749,7 @@ mod test { assert_eq!(deser_set, expected); } + #[cfg(feature = "sets")] #[test] fn deser_hashset() { use ordered_float::OrderedFloat; diff --git a/src/deserialize/parse.rs b/src/deserialize/parse.rs index 7984666..8b5f788 100644 --- a/src/deserialize/parse.rs +++ b/src/deserialize/parse.rs @@ -1,4 +1,6 @@ -use crate::edn::{Edn, Error, List, Map, Set, Vector}; +#[cfg(feature = "sets")] +use crate::edn::Set; +use crate::edn::{Edn, Error, List, Map, Vector}; use std::collections::{BTreeMap, BTreeSet}; const DELIMITERS: [char; 8] = [',', ']', '}', ')', ';', '(', '[', '{']; @@ -413,6 +415,7 @@ fn read_list(chars: &mut std::iter::Enumerate) -> Result) -> Result { let _discard_brackets = chars.next(); let i = chars @@ -438,6 +441,13 @@ fn read_set(chars: &mut std::iter::Enumerate) -> Result) -> Result { + Err(Error::ParseEdn(format!( + "Could not parse set due to feature not being enabled" + ))) +} + fn read_namespaced_map(chars: &mut std::iter::Enumerate) -> Result { let i = chars .clone() @@ -524,11 +534,11 @@ fn read_if_not_container_end( #[cfg(test)] mod test { use super::*; - use crate::edn::{Map, Set}; + use crate::edn::Map; + #[cfg(feature = "sets")] + use crate::edn::Set; use crate::{map, set}; - use ordered_float::OrderedFloat; - #[test] fn parse_empty() { let mut edn = "".chars().enumerate(); @@ -641,6 +651,7 @@ mod test { #[test] fn parse_number() { + use crate::edn; let mut uint = "143".chars().enumerate(); let mut int = "-435143".chars().enumerate(); let mut f = "-43.5143".chars().enumerate(); @@ -650,7 +661,7 @@ mod test { assert_eq!(parse_edn(int.next(), &mut int).unwrap(), Edn::Int(-435143)); assert_eq!( parse_edn(f.next(), &mut f).unwrap(), - Edn::Double(OrderedFloat(-43.5143)) + Edn::Double(edn::Double::from(-43.5143)) ); assert_eq!( parse_edn(r.next(), &mut r).unwrap(), @@ -658,7 +669,7 @@ mod test { ); assert_eq!( parse_edn(big_f64.next(), &mut big_f64).unwrap(), - Edn::Double(OrderedFloat(1e21f64)) + Edn::Double(edn::Double::from(1e21f64)) ); } @@ -854,6 +865,7 @@ mod test { } #[test] + #[cfg(feature = "sets")] fn parse_set() { let mut edn = "#{true \\c 3 }".chars().enumerate(); @@ -868,6 +880,7 @@ mod test { } #[test] + #[cfg(feature = "sets")] fn parse_set_with_commas() { let mut edn = "#{true, \\c, 3,four, }".chars().enumerate(); @@ -883,6 +896,21 @@ mod test { } #[test] + #[cfg(not(feature = "sets"))] + fn parse_set_without_set_feature() { + let mut edn = "#{true, \\c, 3,four, }".chars().enumerate(); + let res = parse(edn.next(), &mut edn); + + assert_eq!( + res, + Err(Error::ParseEdn( + "Could not parse set due to feature not being enabled".to_string() + )) + ) + } + + #[test] + #[cfg(feature = "sets")] fn parse_comment_in_set() { let mut edn = "#{true ; bool true in a set\n \\c 3 }".chars().enumerate(); @@ -897,6 +925,7 @@ mod test { } #[test] + #[cfg(feature = "sets")] fn parse_true_false_nil_with_comments_in_set() { let mut edn = "#{true;this is true\nfalse;this is false\nnil;this is nil\n}" .chars() @@ -909,6 +938,7 @@ mod test { } #[test] + #[cfg(feature = "sets")] fn parse_comment_in_set_end() { let mut edn = "#{true \\c 3; int 3 in a set\n}".chars().enumerate(); @@ -923,6 +953,7 @@ mod test { } #[test] + #[cfg(feature = "sets")] fn parse_complex() { let mut edn = "[:b ( 5 \\c #{true \\c 3 } ) ]".chars().enumerate(); @@ -944,6 +975,7 @@ mod test { } #[test] + #[cfg(feature = "sets")] fn parse_comment_complex() { let mut edn = "[:b ( 5 \\c #{true \\c; char c in a set\n3 } ) ]" .chars() @@ -994,6 +1026,7 @@ mod test { } #[test] + #[cfg(feature = "sets")] fn parse_edn_with_inst() { let mut edn = "#{ :a :b {:c :d :date #inst \"2020-07-16T21:53:14.628-00:00\" ::c ::d} nil}" @@ -1056,6 +1089,7 @@ mod test { } #[test] + #[cfg(feature = "sets")] fn parse_discard_space_invalid() { let mut edn = "#_ ,, #{hello, this will be discarded} #_{so will this} #{this is invalid" .chars() @@ -1357,6 +1391,7 @@ mod test { } #[test] + #[cfg(feature = "sets")] fn parse_tagged_set() { let mut edn = "#domain/model #{1 2 3}".chars().enumerate(); let res = parse(edn.next(), &mut edn).unwrap(); diff --git a/src/edn/mod.rs b/src/edn/mod.rs index 0be4c1b..3c677b9 100644 --- a/src/edn/mod.rs +++ b/src/edn/mod.rs @@ -19,11 +19,13 @@ pub mod utils; /// `EdnType` is an Enum with possible values for an EDN type /// Symbol and Char are not yet implemented /// String implementation of Edn can be obtained with `.to_string()` -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "sets", derive(Eq, PartialOrd, Ord))] #[non_exhaustive] pub enum Edn { Tagged(String, Box), Vector(Vector), + #[cfg(feature = "sets")] Set(Set), Map(Map), List(List), @@ -32,7 +34,7 @@ pub enum Edn { Str(String), Int(i64), UInt(u64), - Double(OrderedFloat), + Double(Double), Rational(String), Char(char), Bool(bool), @@ -57,7 +59,50 @@ impl futures::future::Future for Edn { } } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Clone, Ord, Debug, Eq, PartialEq, PartialOrd, Hash)] +#[cfg(feature = "sets")] +pub struct Double(pub(crate) OrderedFloat); + +#[derive(Clone, Debug, PartialEq)] +#[cfg(not(feature = "sets"))] +pub struct Double(f64); + +impl std::fmt::Display for Double { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +#[cfg(feature = "sets")] +impl Double { + fn to_float(&self) -> f64 { + self.0.into_inner() + } +} + +#[cfg(not(feature = "sets"))] +impl Double { + fn to_float(&self) -> f64 { + self.0 + } +} + +#[cfg(feature = "sets")] +impl From for Double { + fn from(f: f64) -> Self { + Self(OrderedFloat(f)) + } +} + +#[cfg(not(feature = "sets"))] +impl From for Double { + fn from(f: f64) -> Self { + Self(f) + } +} + +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "sets", derive(Eq, PartialOrd, Ord))] pub struct Vector(Vec); impl Vector { #[must_use] @@ -91,7 +136,8 @@ impl futures::future::Future for Vector { } } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "sets", derive(Eq, PartialOrd, Ord))] pub struct List(Vec); impl List { #[must_use] @@ -125,8 +171,11 @@ impl futures::future::Future for List { } } +#[cfg(feature = "sets")] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub struct Set(BTreeSet); + +#[cfg(feature = "sets")] impl Set { #[must_use] pub const fn new(v: BTreeSet) -> Self { @@ -159,7 +208,8 @@ impl futures::future::Future for Set { } } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "sets", derive(Eq, PartialOrd, Ord))] pub struct Map(BTreeMap); impl Map { #[must_use] @@ -227,6 +277,7 @@ impl core::fmt::Display for List { } } +#[cfg(feature = "sets")] impl core::fmt::Display for Set { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!( @@ -264,6 +315,7 @@ impl core::fmt::Display for Edn { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let text = match self { Self::Vector(v) => format!("{v}"), + #[cfg(feature = "sets")] Self::Set(s) => format!("{s}"), Self::Map(m) => format!("{m}"), Self::List(l) => format!("{l}"), @@ -308,7 +360,7 @@ impl Edn { Self::Str(s) => s.parse::().ok(), Self::Int(i) => to_double(i).ok(), Self::UInt(u) => to_double(u).ok(), - Self::Double(d) => Some(d.into_inner()), + Self::Double(d) => Some(d.to_float()), Self::Rational(r) => rational_to_double(r), _ => None, } @@ -336,7 +388,7 @@ impl Edn { #[allow(clippy::cast_possible_wrap)] Self::UInt(u) if i64::try_from(*u).is_ok() => Some(*u as i64), #[allow(clippy::cast_possible_truncation)] - Self::Double(d) => Some((*d).into_inner().round() as i64), + Self::Double(d) => Some((*d).to_float().round() as i64), #[allow(clippy::cast_possible_truncation)] Self::Rational(r) => Some(rational_to_double(r).unwrap_or(0f64).round() as i64), _ => None, @@ -351,11 +403,11 @@ impl Edn { #[allow(clippy::cast_sign_loss)] Self::Int(i) if i > &0 => Some(*i as u64), Self::UInt(i) => Some(*i), - Self::Double(d) if d.into_inner() > 0f64 => + Self::Double(d) if d.to_float() > 0f64 => { #[allow(clippy::cast_sign_loss)] #[allow(clippy::cast_possible_truncation)] - Some((*d).into_inner().round() as u64) + Some((*d).to_float().round() as u64) } Self::Rational(r) if !r.contains('-') => { @@ -427,6 +479,7 @@ impl Edn { }) .collect::>(), ), + #[cfg(feature = "sets")] Self::Set(_) => Some( self.iter_some()? .map(|e| match e { @@ -454,6 +507,7 @@ impl Edn { .map(Self::to_int) .collect::>>()?, ), + #[cfg(feature = "sets")] Self::Set(_) if !self.iter_some()?.any(|e| e.to_int().is_none()) => Some( self.iter_some()? .map(Self::to_int) @@ -478,6 +532,7 @@ impl Edn { .map(Self::to_uint) .collect::>>()?, ), + #[cfg(feature = "sets")] Self::Set(_) if !self.iter_some()?.any(|e| e.to_uint().is_none()) => Some( self.iter_some()? .map(Self::to_uint) @@ -502,6 +557,7 @@ impl Edn { .map(Self::to_float) .collect::>>()?, ), + #[cfg(feature = "sets")] Self::Set(_) if !self.iter_some()?.any(|e| e.to_float().is_none()) => Some( self.iter_some()? .map(Self::to_float) @@ -526,6 +582,7 @@ impl Edn { .map(Self::to_bool) .collect::>>()?, ), + #[cfg(feature = "sets")] Self::Set(_) if !self.iter_some()?.any(|e| e.to_bool().is_none()) => Some( self.iter_some()? .map(Self::to_bool) @@ -642,6 +699,7 @@ impl Edn { /// `set_iter` returns am `Option>` with `Some` for type `Edn::Set` /// Other types return `None` + #[cfg(feature = "sets")] #[must_use] pub fn set_iter(&self) -> Option> { match self { @@ -831,7 +889,7 @@ mod test { #[test] fn iterator() { let v = Edn::Vector(Vector::new(vec![Edn::Int(5), Edn::Int(6), Edn::Int(7)])); - let sum = v + let sum: i64 = v .iter_some() .unwrap() .filter(|e| e.to_int().is_some()) diff --git a/src/edn/utils/index.rs b/src/edn/utils/index.rs index f083456..d213a66 100644 --- a/src/edn/utils/index.rs +++ b/src/edn/utils/index.rs @@ -151,6 +151,7 @@ impl<'a> fmt::Display for Type<'a> { Edn::Int(_) | Edn::UInt(_) => formatter.write_str("integer"), Edn::Str(_) => formatter.write_str("string"), Edn::Vector(_) => formatter.write_str("vector"), + #[cfg(feature = "sets")] Edn::Set(_) => formatter.write_str("set"), Edn::List(_) => formatter.write_str("list"), Edn::Map(_) => formatter.write_str("map"), diff --git a/src/lib.rs b/src/lib.rs index 569ac41..eab7ad7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,7 +76,9 @@ pub fn json_to_edn(json: String) -> String { pub use deserialize::{from_edn, from_str, Deserialize}; pub use edn::Error as EdnError; -pub use edn::{Edn, List, Map, Set, Vector}; +#[cfg(feature = "sets")] +pub use edn::Set; +pub use edn::{Edn, List, Map, Vector}; pub use serialize::Serialize; /// Function for converting Rust types into EDN Strings. diff --git a/tests/parse.rs b/tests/parse.rs index 480dbe6..6ca8ace 100644 --- a/tests/parse.rs +++ b/tests/parse.rs @@ -2,7 +2,9 @@ mod tests { use std::collections::{BTreeMap, BTreeSet}; - use edn_rs::{edn, map, set, Edn, List, Map, Set, Vector}; + #[cfg(feature = "sets")] + use edn_rs::Set; + use edn_rs::{edn, map, set, Edn, List, Map, Vector}; #[test] fn parse_primitive_types() { @@ -25,6 +27,7 @@ mod tests { fn parse_empty_structures() { assert_eq!(edn!([]), Edn::Vector(Vector::new(Vec::new()))); assert_eq!(edn!(()), Edn::List(List::new(Vec::new()))); + #[cfg(feature = "sets")] assert_eq!(edn!(#{}), Edn::Set(Set::new(BTreeSet::new()))); assert_eq!(edn!({}), Edn::Map(Map::new(BTreeMap::new()))); } @@ -60,6 +63,7 @@ mod tests { } #[test] + #[cfg(feature = "sets")] fn parse_simple_set() { let expected = Edn::Set(Set::new(set! { Edn::Int(1), @@ -134,6 +138,7 @@ mod tests { } #[test] + #[cfg(feature = "sets")] fn parse_complex_set() { let expected = Edn::Set(Set::new(set! { Edn::Int(1),