rational_extensions

Extends num_rational::Ratio<T>.
git clone https://git.philomathiclife.com/repos/rational_extensions
Log | Files | Refs | README

commit 965537cea62d23173efdd443eeb3f96a1533436f
parent 1dafc20564a95d3fc9ddf61c360a7aed12e69119
Author: Zack Newman <zack@philomathiclife.com>
Date:   Tue, 11 Apr 2023 19:39:18 -0600

make serde an optional dep

Diffstat:
MCargo.toml | 4++--
Msrc/lib.rs | 104++++++-------------------------------------------------------------------------
Asrc/serde.rs | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 106 insertions(+), 99 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" name = "rational_extensions" readme = "README.md" repository = "https://git.philomathiclife.com/repos/rational_extensions/" -version = "0.3.0" +version = "0.4.0" [lib] name = "rational_extensions" @@ -19,7 +19,7 @@ path = "src/lib.rs" num-integer = { version = "0.1.45", default-features = false } num-rational = { version = "0.4.1", default-features = false } num-traits = { version = "0.2.15", default-features = false } -serde = { version = "1.0.159", default-features = false } +serde = { version = "1.0.160", default-features = false, optional = true } [dev-dependencies] serde_json = { version = "1.0.95", default-features = false, features = ["alloc"] } diff --git a/src/lib.rs b/src/lib.rs @@ -3,7 +3,7 @@ //! ability to constrain the minimum and maximum number of fractional //! digits allowed. #![no_std] -#![no_implicit_prelude] +#![feature(doc_cfg)] #![deny( unsafe_code, unused, @@ -15,34 +15,16 @@ )] #![allow( clippy::implicit_return, - clippy::missing_trait_methods, clippy::question_mark_used, clippy::unseparated_literal_suffix )] extern crate alloc; -#[allow(unused_extern_crates)] -extern crate core; -#[allow(unused_extern_crates)] -extern crate num_integer; -#[allow(unused_extern_crates)] -extern crate num_rational; -#[allow(unused_extern_crates)] -extern crate num_traits; -#[allow(unused_extern_crates)] -extern crate serde; use crate::FromDecStrErr::{IntParseErr, TooFewFractionalDigits, TooManyFractionalDigits}; use alloc::string::{String, ToString}; use alloc::vec::Vec; -use core::clone::Clone; -use core::cmp::PartialOrd; -use core::convert::From; use core::fmt::{self, Debug, Display, Formatter}; -use core::marker::PhantomData; use core::ops::Mul; -use core::option::Option; -use core::result::Result::{self, Err, Ok}; use core::str::FromStr; -use core::{unreachable, write}; use num_integer::Integer; use num_rational::Ratio; use num_traits::Pow; @@ -348,74 +330,14 @@ where // the passed value plus optionally the single byte encodings of ".", "-", and "0". unsafe { String::from_utf8_unchecked(v) } } -use serde::de::{self, Deserialize, Deserializer, Unexpected, Visitor}; -/// Wrapper around a `num_rational::Ratio` that -/// deserializes a JSON string representing a rational number in -/// rational or decimal notation to a Ratio&lt;T&gt;. -#[allow(clippy::exhaustive_structs)] -pub struct Rational<T>(pub Ratio<T>); -#[allow(clippy::single_char_lifetime_names)] -impl<'de, T> Deserialize<'de> for Rational<T> -where - T: Clone - + From<u8> - + FromStr - + Integer - + for<'a> Mul<&'a T, Output = T> - + Pow<usize, Output = T>, -{ - #[inline] - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> - where - D: Deserializer<'de>, - { - /// Visitor used to deserialize a JSON string into a Rational. - struct RationalVisitor<T> { - /// Does not own nor drop a `T`. - _x: PhantomData<fn() -> T>, - } - #[allow(clippy::single_char_lifetime_names)] - impl<'de, T> Visitor<'de> for RationalVisitor<T> - where - T: Clone - + From<u8> - + FromStr - + Integer - + for<'a> Mul<&'a T, Output = T> - + Pow<usize, Output = T>, - { - type Value = Rational<T>; - fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { - formatter.write_str("struct Rational") - } - fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> - where - E: de::Error, - { - #[allow(clippy::unnecessary_wraps)] - /// Used as the closure that maps a `Ratio<R>`into an `Ok(Rational<R>)`. - #[inline] - const fn to_rational<R, E2>(val: Ratio<R>) -> Result<Rational<R>, E2> { - Ok(Rational(val)) - } - try_from_str(v).map_or_else( - |_| { - Err(E::invalid_value( - Unexpected::Str(v), - &"a rational number in fraction or decimal notation", - )) - }, - to_rational, - ) - } - } - deserializer.deserialize_str(RationalVisitor { _x: PhantomData }) - } -} +#[cfg(feature = "serde")] +#[doc(cfg(feature = "serde"))] +/// Enables deserialization of strings in decimal or fractional format +/// into [`serde::Rational<T>`]. +pub mod serde; + #[cfg(test)] mod tests { - #[allow(unused_extern_crates)] - extern crate serde_json; use super::*; use alloc::format; use core::assert_eq; @@ -468,16 +390,4 @@ mod tests { ); Ok(()) } - #[test] - fn test_serde() -> Result<(), serde_json::Error> { - assert_eq!( - Ratio::new(2u8, 3u8), - serde_json::from_str::<Rational::<u8>>(r#""2/3""#)?.0 - ); - assert_eq!( - Ratio::new(67u8, 100u8), - serde_json::from_str::<Rational::<u8>>(r#""0.67""#)?.0 - ); - Ok(()) - } } diff --git a/src/serde.rs b/src/serde.rs @@ -0,0 +1,97 @@ +#![deny( + unsafe_code, + unused, + warnings, + clippy::all, + clippy::cargo, + clippy::nursery, + clippy::pedantic +)] +#![allow(clippy::implicit_return, clippy::missing_trait_methods)] +use crate::{try_from_str, Ratio}; +use core::fmt::{self, Formatter}; +use core::marker::PhantomData; +use core::ops::Mul; +use core::str::FromStr; +use num_integer::Integer; +use num_traits::Pow; +use serde::de::{self, Deserialize, Deserializer, Unexpected, Visitor}; +/// Wrapper around a `num_rational::Ratio` that +/// deserializes a JSON string representing a rational number in +/// fractional or decimal notation to a Ratio&lt;T&gt;. +#[allow(clippy::exhaustive_structs)] +pub struct Rational<T>(pub Ratio<T>); +#[allow(clippy::single_char_lifetime_names)] +impl<'de, T> Deserialize<'de> for Rational<T> +where + T: Clone + + From<u8> + + FromStr + + Integer + + for<'a> Mul<&'a T, Output = T> + + Pow<usize, Output = T>, +{ + #[inline] + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + /// Visitor used to deserialize a JSON string into a Rational. + struct RationalVisitor<T> { + /// Does not own nor drop a `T`. + _x: PhantomData<fn() -> T>, + } + #[allow(clippy::single_char_lifetime_names)] + impl<'de, T> Visitor<'de> for RationalVisitor<T> + where + T: Clone + + From<u8> + + FromStr + + Integer + + for<'a> Mul<&'a T, Output = T> + + Pow<usize, Output = T>, + { + type Value = Rational<T>; + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + formatter.write_str("struct Rational") + } + fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> + where + E: de::Error, + { + #[allow(clippy::unnecessary_wraps)] + /// Used as the closure that maps a `Ratio<R>`into an `Ok(Rational<R>)`. + #[inline] + const fn to_rational<R, E2>(val: Ratio<R>) -> Result<Rational<R>, E2> { + Ok(Rational(val)) + } + try_from_str(v).map_or_else( + |_| { + Err(E::invalid_value( + Unexpected::Str(v), + &"a rational number in fraction or decimal notation", + )) + }, + to_rational, + ) + } + } + deserializer.deserialize_str(RationalVisitor { _x: PhantomData }) + } +} +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_serde() -> Result<(), serde_json::Error> { + assert_eq!( + Ratio::new(2u8, 3u8), + serde_json::from_str::<Rational::<u8>>(r#""2/3""#)?.0 + ); + assert_eq!( + Ratio::new(67u8, 100u8), + serde_json::from_str::<Rational::<u8>>(r#""0.67""#)?.0 + ); + Ok(()) + } +}