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:
M | Cargo.toml | | | 4 | ++-- |
M | src/lib.rs | | | 104 | ++++++------------------------------------------------------------------------- |
A | src/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<T>.
-#[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<T>.
+#[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(())
+ }
+}