From d4e582fe2c6e422d55196d6b0f2d5cd649e5f222 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Thu, 9 Jan 2025 21:27:08 +0000 Subject: [PATCH] feat: document and test newtype and experimental marker `marker has been split out from newtype as I'm unused it has a valid use case, so I've placed under experimental. It is still accessed via `kx_utils::marker` despite the location of the module. --- lcov.info | 22 ++++++++ src/experimental/marker.rs | 72 +++++++++++++++++++++++++ src/experimental/mod.rs | 1 + src/lib.rs | 2 +- src/newtype.rs | 104 +++++++++++++++++++++++++++++-------- 5 files changed, 178 insertions(+), 23 deletions(-) create mode 100644 src/experimental/marker.rs diff --git a/lcov.info b/lcov.info index e69de29..eccc2a1 100644 --- a/lcov.info +++ b/lcov.info @@ -0,0 +1,22 @@ +TN: +SF:/Volumes/workplace/projects/kx-utils/src/experimental/to_string.rs +FN:7,s +FNF:1 +FNDA:0,s +DA:7,0 +DA:8,0 +LF:2 +LH:0 +end_of_record +TN: +SF:/Volumes/workplace/projects/kx-utils/src/newtype.rs +FNF:0 +DA:37,9 +DA:38,9 +DA:42,1 +DA:43,1 +DA:47,0 +DA:48,0 +LF:6 +LH:4 +end_of_record diff --git a/src/experimental/marker.rs b/src/experimental/marker.rs new file mode 100644 index 0000000..a8e0aa2 --- /dev/null +++ b/src/experimental/marker.rs @@ -0,0 +1,72 @@ +// + +/// Defines a marker type with [Clone], [Copy], [Debug], [Display] nad [PartialEq]. +/// +/// # Example +/// +/// ```rust +/// use kx_utils::marker; +/// marker!(Token, "Has no inner value"); +/// let token = Token; +/// ``` +/// +/// This is the equivalent of: +/// +/// ```rust +/// #[doc = "Has no inner value"] +/// #[derive( +/// Clone, +/// Copy, +/// Debug, +/// derive_more::Display, +/// PartialEq, +/// )] +/// pub struct Token; +/// let token = Token; +/// ``` +#[macro_export] +macro_rules! marker { + ($name:ident, $docs:literal) => { + #[doc = $docs] + #[derive(Clone, Copy, Debug, derive_more::Display, PartialEq)] + pub struct $name; + }; +} + +#[cfg(test)] +mod tests { + // a newtype with no inner value + marker!(A, "a type"); + + #[test] + fn marker_is_equals() { + assert_eq!(A, A); + } + + #[test] + fn marker_is_clone() { + let a1 = A; + #[allow(clippy::clone_on_copy)] + let a2 = a1.clone(); + assert_eq!(a1, a2); + } + + #[test] + fn marker_is_copy() { + let a1 = A; + let a2 = a1; + fn consume(_a: A) {} + consume(a1); + assert_eq!(a2, a1); + } + + #[test] + fn marker_is_debug() { + assert_eq!(format!("{:?}", A), "A"); + } + + #[test] + fn marker_is_display() { + assert_eq!(format!("{}", A), "A"); + } +} diff --git a/src/experimental/mod.rs b/src/experimental/mod.rs index 8172f88..d698f63 100644 --- a/src/experimental/mod.rs +++ b/src/experimental/mod.rs @@ -5,6 +5,7 @@ mod kameo; #[cfg(feature = "use-kxio")] mod kxio; +mod marker; mod to_string; #[allow(unused_imports)] diff --git a/src/lib.rs b/src/lib.rs index 9545b96..17d088a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,3 @@ // -mod experimental; +pub mod experimental; mod newtype; diff --git a/src/newtype.rs b/src/newtype.rs index e5c2f99..addf10f 100644 --- a/src/newtype.rs +++ b/src/newtype.rs @@ -1,27 +1,24 @@ // +/// Defines a new type that wraps another type. +/// +/// You can specify an optional number of additional traits to be derived after the wrapped type. +/// +/// # Example +/// +/// ```rust +/// use kx_utils::newtype; +/// newtype!(Username, String, derive_more::Display, "The login name for the user"); +/// let username = Username::new("bob"); +/// assert_eq!(username.as_ref(), "bob"); +/// assert_eq!(format!("{username}"), r"bob"); // because `derive_more::Display` +/// assert_eq!(format!("{username:?}"), r#"Username("bob")"#); +/// let peeled = username.peel(); // moves value out of `username` +/// assert_eq!(peeled, "bob".to_string()); +/// ``` +/// +/// In this example, [Username] also derives [derive_more::Display]. #[macro_export] macro_rules! newtype { - ($name:ident, $docs:literal) => { - #[doc = $docs] - #[derive( - Clone, - Copy, - Default, - Debug, - derive_more::Display, - derive_more::From, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - derive_more::AsRef, - derive_more::Constructor, - serde::Serialize, - serde::Deserialize, - )] - pub struct $name; - }; ($name:ident, $type:ty $(, $derive:ty)*, $docs:literal) => { #[doc = $docs] #[derive( @@ -31,7 +28,6 @@ macro_rules! newtype { PartialEq, Eq, derive_more::AsRef, - derive_more::Deref, serde::Serialize, serde::Deserialize, $($derive),* @@ -54,3 +50,67 @@ macro_rules! newtype { } }; } + +#[cfg(test)] +mod tests { + + use derive_more::Display; + + // a newtype with a String inner value + newtype!(A, String, "a type"); + + #[test] + fn newtype_reflexive() { + let a = A::new("bob"); + assert_eq!(a, a); + } + #[test] + fn newtype_new_is_equal_from() { + assert_eq!(A::new("bob"), A::from("bob".to_string())); + } + + #[test] + fn newtype_is_clone() { + let a1 = A::new("bob"); + let a2 = a1.clone(); + assert_eq!(a1, a2); + } + + #[test] + fn newtype_is_debug() { + assert_eq!(format!("{:?}", A::new("bob")), r#"A("bob")"#); + } + + #[test] + fn newtype_with_derive_display_is_display() { + newtype!(B, String, Display, "displayable"); + assert_eq!(format!("{}", B::new("bob")), r"bob"); + } + + #[test] + fn newtype_is_peelable() { + let a = A::new("bob"); + assert_eq!(a.peel(), "bob"); + } + + #[test] + fn newtype_as_ref() { + let a = A::new("bob"); + let b: &str = a.as_ref(); + assert_eq!(b, "bob"); + } + + #[test] + fn newtype_serialize() { + let a = A::new("bob"); + let s = serde_json::to_string(&a).unwrap(); + assert_eq!(s, "\"bob\""); + } + + #[test] + fn newtype_deserialize() { + let a = A::new("bob"); + let s: A = serde_json::from_str("\"bob\"").unwrap(); + assert_eq!(s, a); + } +}