diff --git a/examples/to_vec.rs b/examples/to_vec.rs index 9c27952..1922ef1 100644 --- a/examples/to_vec.rs +++ b/examples/to_vec.rs @@ -5,7 +5,7 @@ fn to_vec() { let v = Edn::List(List::new(vec![ Edn::Key(":my-key".to_string()), Edn::Int(6), - Edn::Rational((7, 4)), + Edn::Rational("7/4".to_string()), ])); println!("{:?}", v.to_vec().unwrap()); diff --git a/examples/tokenize_edn.rs b/examples/tokenize_edn.rs index 2221144..3cf4fd2 100644 --- a/examples/tokenize_edn.rs +++ b/examples/tokenize_edn.rs @@ -12,7 +12,7 @@ fn tokenize() { Edn::Bool(false), Edn::Key(":f".to_string()), Edn::Nil, - Edn::Rational((3, 4)), + Edn::Rational("3/4".to_string()), ])); println!("{edn:?}"); diff --git a/src/deserialize/parse.rs b/src/deserialize/parse.rs index b392a25..3cdd221 100644 --- a/src/deserialize/parse.rs +++ b/src/deserialize/parse.rs @@ -187,23 +187,6 @@ fn read_discard(chars: &mut iter::Enumerate>) -> Result) -> Option<(i64, u64)> { - let slice = slice.as_ref(); - let index = slice.find('/'); - - if let Some(i) = index { - let (num, den) = slice.split_at(i); // This can't panic because the index is valid - let num = num.parse::(); - let den = den[1..].parse::(); - - if let (Ok(n), Ok(d)) = (num, den) { - return Some((n, d)); - } - return None; - } - None -} - fn read_number(n: char, chars: &mut iter::Enumerate>) -> Result { let c_len = chars .clone() @@ -275,7 +258,9 @@ fn read_number(n: char, chars: &mut iter::Enumerate>) -> Re Ok(Edn::Int(i64::from_str_radix(&n, radix)?)) } n if n.parse::().is_ok() => Ok(Edn::Double(n.parse::()?.into())), - n if num_den_from_slice(&n).is_some() => Ok(Edn::Rational(num_den_from_slice(n).unwrap())), + n if n.contains('/') && n.split('/').all(|d| d.parse::().is_ok()) => { + Ok(Edn::Rational(n)) + } n if n.to_uppercase().chars().filter(|c| c == &'E').count() > 1 => { let mut n = n.chars(); read_symbol(n.next().unwrap_or(' '), &mut n.enumerate()) diff --git a/src/edn/mod.rs b/src/edn/mod.rs index 2fa6be6..7436a13 100644 --- a/src/edn/mod.rs +++ b/src/edn/mod.rs @@ -38,7 +38,7 @@ pub enum Edn { Int(i64), UInt(u64), Double(Double), - Rational((i64, u64)), + Rational(String), Char(char), Bool(bool), Nil, @@ -254,7 +254,7 @@ impl core::fmt::Display for Edn { Self::Int(i) => format!("{i}"), Self::UInt(u) => format!("{u}"), Self::Double(d) => format!("{d}"), - Self::Rational((n, d)) => format!("{n}/{d}"), + Self::Rational(r) => r.to_string(), Self::Bool(b) => format!("{b}"), Self::Char(c) => char_to_edn(*c), Self::Nil => String::from("nil"), @@ -271,7 +271,7 @@ impl Edn { /// use edn_rs::edn::{Edn, Vector}; /// /// let key = Edn::Key(String::from(":1234")); - /// let q = Edn::Rational((3, 4)); + /// let q = Edn::Rational(String::from("3/4")); /// let i = Edn::Int(12i64); /// /// assert_eq!(Edn::Vector(Vector::empty()).to_float(), None); @@ -287,7 +287,7 @@ impl Edn { Self::Int(i) => to_double(i).ok(), Self::UInt(u) => to_double(u).ok(), Self::Double(d) => Some(d.to_float()), - Self::Rational(r) => Some(rational_to_double(*r)), + Self::Rational(r) => rational_to_double(r), _ => None, } } @@ -297,7 +297,7 @@ impl Edn { /// use edn_rs::edn::{Edn, Vector}; /// /// let key = Edn::Key(String::from(":1234")); - /// let q = Edn::Rational((3, 4)); + /// let q = Edn::Rational(String::from("3/4")); /// let f = Edn::Double(12.3f64.into()); /// /// assert_eq!(Edn::Vector(Vector::empty()).to_float(), None); @@ -313,12 +313,12 @@ impl Edn { Self::Int(i) => Some(*i), #[allow(clippy::cast_possible_wrap)] Self::UInt(u) if i64::try_from(*u).is_ok() => Some(*u as i64), - #[cfg(feature = "std")] #[allow(clippy::cast_possible_truncation)] - Self::Double(d) => Some((*d).to_float().round() as i64), #[cfg(feature = "std")] + Self::Double(d) => Some((*d).to_float().round() as i64), #[allow(clippy::cast_possible_truncation)] - Self::Rational(r) => Some(rational_to_double(*r).round() as i64), + #[cfg(feature = "std")] + Self::Rational(r) => Some(rational_to_double(r).unwrap_or(0f64).round() as i64), _ => None, } } @@ -339,11 +339,11 @@ impl Edn { Some((*d).to_float().round() as u64) } #[cfg(feature = "std")] - Self::Rational(r) if r.0 > 0 => + Self::Rational(r) if !r.contains('-') => { #[allow(clippy::cast_sign_loss)] #[allow(clippy::cast_possible_truncation)] - Some(rational_to_double(*r).round() as u64) + Some(rational_to_double(r)?.round() as u64) } _ => None, } @@ -707,9 +707,17 @@ where format!("{i:?}").parse::() } -#[allow(clippy::cast_precision_loss)] -pub(crate) fn rational_to_double((n, d): (i64, u64)) -> f64 { - (n as f64) / (d as f64) +pub(crate) fn rational_to_double(r: &str) -> Option { + if r.split('/').count() == 2 { + let vals = r + .split('/') + .map(ToString::to_string) + .map(|v| v.parse::()) + .map(Result::ok) + .collect::>>()?; + return Some(vals[0] / vals[1]); + } + None } #[derive(Debug, PartialEq, Eq)] @@ -775,6 +783,15 @@ mod test { use alloc::vec; use super::*; + #[test] + fn parses_rationals() { + assert_eq!(rational_to_double("3/4").unwrap(), 0.75f64); + assert_eq!(rational_to_double("25/5").unwrap(), 5f64); + assert_eq!(rational_to_double("15/4").unwrap(), 3.75f64); + assert_eq!(rational_to_double("3 4"), None); + assert_eq!(rational_to_double("3/4/5"), None); + assert_eq!(rational_to_double("text/moretext"), None); + } #[test] fn iterator() { diff --git a/src/json/mod.rs b/src/json/mod.rs index 288984b..3b4bf95 100644 --- a/src/json/mod.rs +++ b/src/json/mod.rs @@ -28,7 +28,7 @@ pub fn display_as_json(edn: &Edn) -> String { } s } - Edn::Rational(r) => format!("{}", rational_to_double(*r)), + Edn::Rational(r) => format!("{}", rational_to_double(r).unwrap()), Edn::Char(c) => format!("'{c}'"), Edn::Bool(b) => format!("{b}"), Edn::Nil => String::from("null"), @@ -142,11 +142,11 @@ mod test { #[test] fn rational_numbers() { assert_eq!( - display_as_json(&Edn::Rational((3, 4))), + display_as_json(&Edn::Rational("3/4".to_string())), String::from("0.75") ); assert_eq!( - display_as_json(&Edn::Rational((-3, 9))), + display_as_json(&Edn::Rational("-3/9".to_string())), String::from("-0.3333333333333333") ); } @@ -189,7 +189,7 @@ mod test { Edn::Key(":b".to_string()), Edn::Str("test".to_string()), Edn::Char('4'), - Edn::Rational((-3, 4)), + Edn::Rational("-3/4".to_string()), Edn::Double(4.5f64.into()), Edn::UInt(4), ])); @@ -206,7 +206,7 @@ mod test { Edn::Key(":b".to_string()), Edn::Str("test".to_string()), Edn::Char('4'), - Edn::Rational((-3, 4)), + Edn::Rational("-3/4".to_string()), Edn::Double(4.5f64.into()), Edn::UInt(4), ])); @@ -223,7 +223,7 @@ mod test { Edn::Key(":my-bestie".to_string()), Edn::Str("test".to_string()), Edn::Char('4'), - Edn::Rational((-3, 4)), + Edn::Rational("-3/4".to_string()), Edn::Double(4.5f64.into()), Edn::UInt(4), ])); @@ -238,7 +238,7 @@ mod test { fn simple_map() { let map = Edn::Map(Map::new(map! { String::from("1.2") => Edn::Bool(false), - String::from(":belo-monte") => Edn::Rational((3, 4)), + String::from(":belo-monte") => Edn::Rational(String::from("3/4")), String::from("true") => Edn::Char('d') })); @@ -258,14 +258,14 @@ mod test { Edn::Bool(false), Edn::Key(":f".to_string()), Edn::Nil, - Edn::Rational((3, 4)), + Edn::Rational("3/4".to_string()), Edn::Set(Set::new(set! { - Edn::Rational((3, 4)) + Edn::Rational("3/4".to_string()) })), ])), Edn::Map(Map::new(map![ String::from("false") => Edn::Key(":f".to_string()), - String::from("nil") => Edn::Rational((3, 4)), + String::from("nil") => Edn::Rational("3/4".to_string()), String::from(":my-crazy-map") => Edn::Map(Map::new(map![ String::from("false") => Edn::Map( Map::new( map![ @@ -273,7 +273,7 @@ mod test { ])), String::from("nil") => Edn::Vector( Vector::new( vec![ - Edn::Rational((3, 4)), + Edn::Rational("3/4".to_string()), Edn::Int(1i64) ])) ])) @@ -293,7 +293,7 @@ mod test { Edn::Key(":b".to_string()), Edn::Str("test".to_string()), Edn::Char('4'), - Edn::Rational((-3, 4)), + Edn::Rational("-3/4".to_string()), Edn::Double(4.5f64.into()), Edn::UInt(4), ]))), diff --git a/src/macros/mod.rs b/src/macros/mod.rs index c782d3f..3227180 100644 --- a/src/macros/mod.rs +++ b/src/macros/mod.rs @@ -21,7 +21,7 @@ /// Edn::Bool(false), /// Edn::Key(":f".to_string()), /// Edn::Nil, -/// Edn::Rational((3, 4)) +/// Edn::Rational("3/4".to_string()) /// ] /// ) /// ); @@ -38,7 +38,7 @@ /// Edn::Bool(false), /// Edn::Key(":f".to_string()), /// Edn::Nil, -/// Edn::Rational((3, 4)) +/// Edn::Rational("3/4".to_string()) /// } /// ) /// ); @@ -50,7 +50,7 @@ /// map!{ /// String::from("1.2") => Edn::Bool(false), /// // Note `:b` becomes `b` -/// String::from(":b") => Edn::Rational((3, 4)) +/// String::from(":b") => Edn::Rational(String::from("3/4")) /// } /// ) /// ); @@ -81,7 +81,7 @@ /// ])), /// String::from("nil") => Edn::Vector( /// Vector::new( vec![ -/// Edn::Rational((3, 4)), +/// Edn::Rational("3/4".to_string()), /// Edn::Int(1i64) /// ])) /// ])) @@ -200,7 +200,8 @@ macro_rules! edn_internal { }; ($num:tt/$den:tt) => {{ - Edn::Rational(($num, $den)) + let q = std::format!("{:?}/{:?}", $num, $den); + Edn::Rational(q) }}; (:$key:tt) => {{ diff --git a/tests/deserialize.rs b/tests/deserialize.rs index ce206e2..d8ad39c 100644 --- a/tests/deserialize.rs +++ b/tests/deserialize.rs @@ -131,7 +131,10 @@ mod test { Edn::from_str("-43.5143").unwrap(), Edn::Double(edn::Double::from(-43.5143)) ); - assert_eq!(Edn::from_str("43/5143").unwrap(), Edn::Rational((43, 5143))); + assert_eq!( + Edn::from_str("43/5143").unwrap(), + Edn::Rational("43/5143".to_string()) + ); assert_eq!( Edn::from_str("999999999999999999999.0").unwrap(), Edn::Double(edn::Double::from(1e21f64)) diff --git a/tests/emit_json.rs b/tests/emit_json.rs index 2e91733..092df3e 100644 --- a/tests/emit_json.rs +++ b/tests/emit_json.rs @@ -110,14 +110,14 @@ mod tests { Edn::Bool(false), Edn::Key(":f".to_string()), Edn::Nil, - Edn::Rational((3, 4)), + Edn::Rational("3/4".to_string()), Edn::Set(Set::new(set! { - Edn::Rational((3, 4)) + Edn::Rational("3/4".to_string()) })), ])), Edn::Map(Map::new(map![ String::from("false") => Edn::Key(":f".to_string()), - String::from("nil") => Edn::Rational((3, 4)), + String::from("nil") => Edn::Rational("3/4".to_string()), String::from(":my-crazy-map") => Edn::Map(Map::new(map![ String::from("false") => Edn::Map( Map::new( map![ @@ -125,7 +125,7 @@ mod tests { ])), String::from("nil") => Edn::Vector( Vector::new( vec![ - Edn::Rational((3, 4)), + Edn::Rational("3/4".to_string()), Edn::Int(1i64) ])) ])) diff --git a/tests/parse.rs b/tests/parse.rs index fbe36e9..f3fb1c8 100644 --- a/tests/parse.rs +++ b/tests/parse.rs @@ -15,7 +15,7 @@ mod tests { edn!("this is a string"), Edn::Str("this is a string".to_string()) ); - assert_eq!(edn!(3 / 4), Edn::Rational((3, 4))); + assert_eq!(edn!(3 / 4), Edn::Rational("3/4".to_string())); assert_eq!(edn!(true), Edn::Bool(true)); assert_eq!(edn!(false), Edn::Bool(false)); assert_eq!(edn!(nil), Edn::Nil); @@ -39,7 +39,7 @@ mod tests { Edn::Bool(false), Edn::Key(":f".to_string()), Edn::Nil, - Edn::Rational((3, 4)), + Edn::Rational("3/4".to_string()), ])); assert_eq!(edn!([ sym 1.2 3 false :f nil 3/4]), expected); @@ -54,7 +54,7 @@ mod tests { Edn::Bool(false), Edn::Key(":f".to_string()), Edn::Nil, - Edn::Rational((3, 4)), + Edn::Rational("3/4".to_string()), ])); assert_eq!(edn!((1 1.2 3 false :f nil 3/4)), expected); @@ -64,7 +64,7 @@ mod tests { fn parse_simple_map() { let expected = Edn::Map(Map::new(map! { String::from("1.2") => Edn::Bool(false), - String::from(":b") => Edn::Rational((3, 4)) + String::from(":b") => Edn::Rational(String::from("3/4")) })); assert_eq!(edn!({1.2 false, :b 3/4}), expected); @@ -80,7 +80,7 @@ mod tests { Edn::Bool(false), Edn::Key(":f".to_string()), Edn::Nil, - Edn::Rational((3, 4)), + Edn::Rational("3/4".to_string()), ])), ])); @@ -97,7 +97,7 @@ mod tests { Edn::Bool(false), Edn::Key(":f".to_string()), Edn::Nil, - Edn::Rational((3, 4)), + Edn::Rational("3/4".to_string()), ])), ])); @@ -112,7 +112,7 @@ mod tests { Edn::Int(3), Edn::Map(Map::new(map![ String::from("false") => Edn::Key(":f".to_string()), - String::from("nil") => Edn::Rational((3, 4)) + String::from("nil") => Edn::Rational("3/4".to_string()) ])), ])); @@ -132,7 +132,7 @@ mod tests { ])), String::from("nil") => Edn::Vector( Vector::new( vec![ - Edn::Rational((3, 4)), + Edn::Rational("3/4".to_string()), Edn::Int(1i64) ])) ])), diff --git a/tests/parse_sets.rs b/tests/parse_sets.rs index c9e45be..ecd9874 100644 --- a/tests/parse_sets.rs +++ b/tests/parse_sets.rs @@ -24,7 +24,7 @@ mod tests { Edn::Bool(false), Edn::Key(":f".to_string()), Edn::Nil, - Edn::Rational((3, 4)) + Edn::Rational("3/4".to_string()) })); assert_eq!(edn!(#{1 1.2 3 false :f nil 3/4}), expected); @@ -41,13 +41,13 @@ mod tests { Edn::Bool(false), Edn::Key(":f".to_string()), Edn::Nil, - Edn::Rational((3, 4)) + Edn::Rational("3/4".to_string()) ])), Edn::Vector( Vector::new( vec![ Edn::Bool(true), Edn::Key(":b".to_string()), - Edn::Rational((12, 5)) + Edn::Rational("12/5".to_string()) ])) }));