diff --git a/CHANGELOG.md b/CHANGELOG.md index b67f3fc..49cf429 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ +## [0.8.0](https://github.com/Blobfolio/dactyl/releases/tag/v0.8.0) - 2024-11-28 + +### Changed + +* Bump MSRV to `1.81` +* `NiceInflection::nice_inflect` now requires `&str` arguments instead of `AsRef`; +* `NiceInflection::nice_inflect` now returns a `Display`-friendly wrapper instead of a `String` to avoid intermediary allocations; + + + ## [0.7.4](https://github.com/Blobfolio/dactyl/releases/tag/v0.7.4) - 2024-09-15 ### New diff --git a/CREDITS.md b/CREDITS.md index 560d999..a2bfb0f 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -1,6 +1,6 @@ # Project Dependencies Package: dactyl - Version: 0.7.4 - Generated: 2024-09-15 08:15:40 UTC + Version: 0.8.0 + Generated: 2024-11-28 18:21:00 UTC -This package has no dependencies. +This project has no dependencies. diff --git a/Cargo.toml b/Cargo.toml index 383e8de..2c0d82c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "dactyl" -version = "0.7.4" -authors = ["Blobfolio, LLC. "] +version = "0.8.0" +authors = ["Josh Stoik "] edition = "2021" -rust-version = "1.70" +rust-version = "1.81" description = "A small library to quickly stringify integers with basic formatting." license = "WTFPL" repository = "https://github.com/Blobfolio/dactyl" @@ -23,9 +23,6 @@ default-target = "x86_64-unknown-linux-gnu" [package.metadata.bashman] name = "Dactyl" -bash-dir = "./" -man-dir = "./" -credits-dir = "./" [dev-dependencies] brunch = "0.6.*" diff --git a/README.md b/README.md index 2ebdb25..e187a17 100644 --- a/README.md +++ b/README.md @@ -49,29 +49,5 @@ Add `dactyl` to your `dependencies` in `Cargo.toml`, like: ``` [dependencies] -dactyl = "0.7.*" +dactyl = "0.8.*" ``` - - - -## License - -See also: [CREDITS.md](CREDITS.md) - -Copyright © 2024 [Blobfolio, LLC](https://blobfolio.com) <hello@blobfolio.com> - -This work is free. You can redistribute it and/or modify it under the terms of the Do What The Fuck You Want To Public License, Version 2. - - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - Version 2, December 2004 - - Copyright (C) 2004 Sam Hocevar - - Everyone is permitted to copy and distribute verbatim or modified - copies of this license document, and changing it is allowed as long - as the name is changed. - - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/build.rs b/build.rs index d4fc377..4a54ea6 100644 --- a/build.rs +++ b/build.rs @@ -179,6 +179,8 @@ macro_rules! wrt_self { fn main() { + println!("cargo:rerun-if-env-changed=CARGO_PKG_VERSION"); + // Make sure our formatting looks right. assert_eq!(AnyNum::from(12345_u32).to_string(), "12_345", "Bug: Number formatting is wrong!"); assert_eq!(AnyNum::from(-12345_i32).to_string(), "-12_345", "Bug: Number formatting is wrong!"); diff --git a/src/hash.rs b/src/hash.rs index cb159a2..efb561f 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -2,6 +2,8 @@ # Dactyl: Hashing */ +#![expect(clippy::cast_lossless, reason = "False positive.")] + use std::hash::{ BuildHasherDefault, Hasher, @@ -123,7 +125,6 @@ pub struct NoHasher(u64); /// # Helper: Write Method(s) for Unsigned Ints. macro_rules! write_unsigned { ($($fn:ident, $ty:ty),+ $(,)?) => ($( - #[allow(clippy::cast_lossless)] // "False positive." #[inline] #[doc = concat!("# Write `", stringify!($ty), "`")] fn $fn(&mut self, val: $ty) { @@ -136,8 +137,7 @@ macro_rules! write_unsigned { /// # Helper: Write Method(s) for Signed Ints. macro_rules! write_signed { ($($fn:ident, $ty1:ty, $ty2:ty),+ $(,)?) => ($( - #[allow(clippy::cast_lossless)] // False positive. - #[allow(clippy::cast_sign_loss)] // False positive. + #[expect(clippy::cast_sign_loss, reason = "False positive.")] #[inline] #[doc = concat!("# Write `", stringify!($ty1), "`")] fn $fn(&mut self, val: $ty1) { diff --git a/src/lib.rs b/src/lib.rs index 9c58ad8..2514e7a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,7 +45,7 @@ But the niceness doesn't stop there. Dactyl provides several other structs, meth */ #![deny( - // TODO: clippy::allow_attributes_without_reason, + clippy::allow_attributes_without_reason, clippy::correctness, unreachable_pub, unsafe_code, @@ -58,7 +58,7 @@ But the niceness doesn't stop there. Dactyl provides several other structs, meth clippy::perf, clippy::style, - // TODO: clippy::allow_attributes, + clippy::allow_attributes, clippy::clone_on_ref_ptr, clippy::create_dir, clippy::filetype_is_file, @@ -92,8 +92,8 @@ But the niceness doesn't stop there. Dactyl provides several other structs, meth unused_import_braces, )] -#![allow(clippy::module_name_repetitions)] // Repetition is preferred. -#![allow(clippy::redundant_pub_crate)] // Unresolvable. +#![expect(clippy::module_name_repetitions, reason = "Repetition is preferred.")] +#![expect(clippy::redundant_pub_crate, reason = "Unresolvable.")] @@ -147,8 +147,8 @@ const DOUBLE: [[u8; 2]; 100] = [ pub(crate) const fn double(idx: usize) -> [u8; 2] { DOUBLE[idx] } #[inline] -#[allow(clippy::cast_possible_truncation)] // False positive. -#[allow(clippy::integer_division)] // We want this. +#[expect(clippy::cast_possible_truncation, reason = "False positive.")] +#[expect(clippy::integer_division, reason = "We want this.")] /// # Triple Digits. /// /// Return both digits, ASCII-fied. diff --git a/src/nice_elapsed/mod.rs b/src/nice_elapsed/mod.rs index 65326de..f0b0cd3 100644 --- a/src/nice_elapsed/mod.rs +++ b/src/nice_elapsed/mod.rs @@ -127,7 +127,7 @@ impl fmt::Display for NiceElapsed { impl Eq for NiceElapsed {} impl From for NiceElapsed { - #[allow(clippy::cast_possible_truncation)] // False positive. + #[expect(clippy::cast_possible_truncation, reason = "False positive.")] fn from(src: Duration) -> Self { let s = src.as_secs(); let ms = @@ -198,7 +198,7 @@ impl NiceElapsed { } } - #[allow(clippy::cast_possible_truncation)] // False positive. + #[expect(clippy::cast_possible_truncation, reason = "False positive.")] #[must_use] /// # Time Chunks (with Days). /// @@ -226,7 +226,7 @@ impl NiceElapsed { (d, h, m, s) } - #[allow(clippy::cast_possible_truncation)] // False positive. + #[expect(clippy::cast_possible_truncation, reason = "False positive.")] #[must_use] /// # Time Chunks. /// @@ -284,7 +284,7 @@ impl NiceElapsed { /// ``` pub fn as_bytes(&self) -> &[u8] { &self.inner[0..self.len] } - #[allow(unsafe_code)] // Content is ASCII. + #[expect(unsafe_code, reason = "Content is ASCII.")] #[must_use] #[inline] /// # As Str. @@ -308,9 +308,12 @@ impl NiceElapsed { } impl NiceElapsed { - #[allow(clippy::cast_possible_truncation)] // False positive. - #[allow(clippy::many_single_char_names)] // Consistency is preferred. - #[allow(clippy::similar_names)] // Consistency is preferred. + #[expect(clippy::cast_possible_truncation, reason = "False positive.")] + #[expect( + clippy::many_single_char_names, + clippy::similar_names, + reason = "Consistency is preferred.", + )] /// # From DHMS.ms. /// /// Build with days, hours, minutes, seconds, and milliseconds (hundredths). diff --git a/src/nice_int/mod.rs b/src/nice_int/mod.rs index b2ba092..546f9ff 100644 --- a/src/nice_int/mod.rs +++ b/src/nice_int/mod.rs @@ -134,7 +134,7 @@ impl NiceWrapper { /// Return the value as a byte string. pub fn as_bytes(&self) -> &[u8] { &self.inner[self.from..] } - #[allow(unsafe_code)] // Content is ASCII. + #[expect(unsafe_code, reason = "Content is ASCII.")] #[must_use] #[inline] /// # As Str. @@ -203,7 +203,7 @@ macro_rules! nice_parse { } impl $nice { - #[allow(clippy::cast_possible_truncation)] // False positive. + #[expect(clippy::cast_possible_truncation, reason = "False positive.")] /// # Parse. fn parse(&mut self, mut num: $uint) { for chunk in self.inner.rchunks_exact_mut(4) { diff --git a/src/nice_int/nice_float.rs b/src/nice_int/nice_float.rs index 5e8b8c5..38f7354 100644 --- a/src/nice_int/nice_float.rs +++ b/src/nice_int/nice_float.rs @@ -32,7 +32,9 @@ macro_rules! inner { /// `NiceFloat` provides a quick way to convert an `f32` or `f64` (up to /// the absolute equivalent of `u64::MAX`) into a formatted byte string for -/// e.g. printing. Commas are added for every (integer) thousand; decimals are +/// e.g. printing. +/// +/// Commas are added for every (integer) thousand; decimals are /// rounded up to the nearest eight digits using a tie-to-even strategy. /// /// Absolute values larger than `u64::MAX` will print as either @@ -326,7 +328,7 @@ impl NiceFloat { out } - #[allow(unsafe_code)] // Content is ASCII. + #[expect(unsafe_code, reason = "Content is ASCII.")] #[inline] #[must_use] /// # Compact String. @@ -396,7 +398,7 @@ impl NiceFloat { else { self.as_bytes() } } - #[allow(unsafe_code)] // Content is ASCII. + #[expect(unsafe_code, reason = "Content is ASCII.")] #[inline] #[must_use] /// # Precise String. @@ -451,7 +453,7 @@ impl NiceFloat { ) } - #[allow(clippy::cast_possible_truncation)] // False positive. + #[expect(clippy::cast_possible_truncation, reason = "False positive.")] /// # Parse Top. /// /// Write the integer portion of the value. This works the same way as @@ -498,7 +500,7 @@ impl NiceFloat { } } - #[allow(clippy::integer_division)] // We want this. + #[expect(clippy::integer_division, reason = "We want this.")] /// # Parse Bottom. /// /// This writes the fractional part of the float, if any. @@ -623,7 +625,7 @@ impl From for FloatKind { -#[allow(clippy::integer_division)] // We want this. +#[expect(clippy::integer_division, reason = "We want this.")] /// # Parse Finite `f32` /// /// This parses a float (that is not NaN or infinite) into the appropriate @@ -672,7 +674,7 @@ fn parse_finite_f32(num: f32) -> FloatKind { else { FloatKind::Normal(top, bottom, num.is_sign_negative()) } } -#[allow(clippy::integer_division)] // We want this. +#[expect(clippy::integer_division, reason = "We want this.")] /// # Parse Finite `f64` /// /// This parses a float (that is not NaN or infinite) into the appropriate @@ -727,7 +729,7 @@ fn parse_finite_f64(num: f64) -> FloatKind { -#[allow(clippy::cast_possible_truncation)] // False positive. +#[expect(clippy::cast_possible_truncation, reason = "False positive.")] /// # Round, Tie to Even. /// /// Fractions are rounded on the ninth decimal place (to eight places). diff --git a/src/nice_int/nice_percent.rs b/src/nice_int/nice_percent.rs index 056d62f..a9a836b 100644 --- a/src/nice_int/nice_percent.rs +++ b/src/nice_int/nice_percent.rs @@ -66,9 +66,12 @@ impl Default for NicePercent { /// This code is identical for `f32` and `f64` types. macro_rules! nice_from { ($($float:ty),+ $(,)?) => ($( - #[allow(clippy::cast_possible_truncation)] // It is what it is. - #[allow(clippy::cast_sign_loss)] // It is what it is. - #[allow(clippy::integer_division)] // We want this. + #[expect( + clippy::cast_possible_truncation, + clippy::cast_sign_loss, + reason = "It is what it is.", + )] + #[expect(clippy::integer_division, reason = "We want this.")] impl From<$float> for NicePercent { fn from(num: $float) -> Self { // Shortcut for overflowing values. diff --git a/src/nice_int/nice_u16.rs b/src/nice_int/nice_u16.rs index 7f74e5a..4a196c2 100644 --- a/src/nice_int/nice_u16.rs +++ b/src/nice_int/nice_u16.rs @@ -64,9 +64,9 @@ super::nice_default!(NiceU16, ZERO, SIZE); super::nice_from_nz!(NiceU16, NonZeroU16); impl From for NiceU16 { - #[allow(clippy::cast_possible_truncation)] // False positive. - #[allow(clippy::integer_division)] // We want this. - #[allow(clippy::many_single_char_names)] // Consistency is preferred. + #[expect(clippy::cast_possible_truncation, reason = "False positive.")] + #[expect(clippy::integer_division, reason = "We want this.")] + #[expect(clippy::many_single_char_names, reason = "Consistency is preferred.")] fn from(num: u16) -> Self { if 999 < num { let (num, rem) = (num / 1000, num % 1000); diff --git a/src/nice_int/nice_u8.rs b/src/nice_int/nice_u8.rs index 2bec422..1de06e4 100644 --- a/src/nice_int/nice_u8.rs +++ b/src/nice_int/nice_u8.rs @@ -122,7 +122,7 @@ impl NiceU8 { /// ``` pub const fn as_bytes3(&self) -> &[u8] { &self.inner } - #[allow(unsafe_code)] // Content is ASCII. + #[expect(unsafe_code, reason = "Content is ASCII.")] #[must_use] #[inline] /// # Double Digit Str. @@ -149,7 +149,7 @@ impl NiceU8 { unsafe { std::str::from_utf8_unchecked(self.as_bytes2()) } } - #[allow(unsafe_code)] // Content is ASCII. + #[expect(unsafe_code, reason = "Content is ASCII.")] #[must_use] #[inline] /// # Triple Digit Str. diff --git a/src/traits/btoi.rs b/src/traits/btoi.rs index 5342f2b..d640cdf 100644 --- a/src/traits/btoi.rs +++ b/src/traits/btoi.rs @@ -2,6 +2,8 @@ # Dactyl — Bytes to Signed. */ +#![expect(clippy::cast_possible_truncation, reason = "False positive.")] + use crate::traits::BytesToUnsigned; use std::{ cmp::Ordering, @@ -44,8 +46,7 @@ pub trait BytesToSigned: Sized { macro_rules! signed { ($ty:ty, $unsigned:ty, $min:literal, $max:literal) => ( impl BytesToSigned for $ty { - #[allow(clippy::cast_possible_truncation)] // False positive. - #[allow(clippy::cast_possible_wrap)] // False positive. + #[expect(clippy::cast_possible_wrap, reason = "False positive.")] /// # Bytes to Signed. fn btoi(src: &[u8]) -> Option { match src.len() { diff --git a/src/traits/btou.rs b/src/traits/btou.rs index 6184bff..e902ea9 100644 --- a/src/traits/btou.rs +++ b/src/traits/btou.rs @@ -230,8 +230,8 @@ impl BytesToUnsigned for u64 { impl BytesToUnsigned for u128 { #[cfg(target_endian = "little")] - #[allow(clippy::cognitive_complexity)] // Readability. - #[allow(clippy::too_many_lines)] // These numbers are huge. + #[expect(clippy::cognitive_complexity, reason = "Readability.")] + #[expect(clippy::too_many_lines, reason = "These numbers are huge.")] #[must_use] /// # Bytes to Unsigned. fn btou(src: &[u8]) -> Option { @@ -360,7 +360,7 @@ impl BytesToUnsigned for u128 { } -#[allow(clippy::cast_possible_truncation)] // False positive. +#[expect(clippy::cast_possible_truncation, reason = "False positive.")] impl BytesToUnsigned for usize { #[cfg(target_pointer_width = "16")] #[must_use] @@ -437,7 +437,7 @@ const fn parse2(src: &[u8]) -> Option { } #[cfg(target_endian = "little")] -#[allow(clippy::cast_possible_truncation)] // False positive. +#[expect(clippy::cast_possible_truncation, reason = "False positive.")] /// # Parse Four. /// /// This parses four digits as a single `u32`, reducing the number of @@ -470,7 +470,7 @@ const fn parse4(src: &[u8]) -> Option { } #[cfg(target_endian = "little")] -#[allow(clippy::cast_possible_truncation)] // False positive. +#[expect(clippy::cast_possible_truncation, reason = "False positive.")] /// # Parse Eight. /// /// This parses eight digits as a single `u64`, reducing the number of @@ -505,7 +505,7 @@ const fn parse8(src: &[u8]) -> Option { } #[cfg(target_endian = "little")] -#[allow(clippy::cast_possible_truncation)] // False positive. +#[expect(clippy::cast_possible_truncation, reason = "False positive.")] /// # Parse Sixteen. /// /// This parses sixteen digits as a single `u128`, reducing the number of diff --git a/src/traits/hex.rs b/src/traits/hex.rs index ddba781..8f0417f 100644 --- a/src/traits/hex.rs +++ b/src/traits/hex.rs @@ -117,7 +117,7 @@ impl HexToUnsigned for usize { } #[cfg(target_pointer_width = "32")] -#[allow(clippy::cast_possible_truncation)] // False positive. +#[expect(clippy::cast_possible_truncation, reason = "False positive.")] impl HexToUnsigned for usize { #[inline] /// # Hex (Bytes) to Unsigned. @@ -125,7 +125,7 @@ impl HexToUnsigned for usize { } #[cfg(target_pointer_width = "64")] -#[allow(clippy::cast_possible_truncation)] // False positive. +#[expect(clippy::cast_possible_truncation, reason = "False positive.")] impl HexToUnsigned for usize { #[inline] /// # Hex (Bytes) to Unsigned. @@ -238,7 +238,7 @@ macro_rules! signed { ($signed:ty, $unsigned:ty) => ( impl HexToSigned for $signed { #[inline] - #[allow(clippy::cast_possible_wrap)] // False positive. + #[expect(clippy::cast_possible_wrap, reason = "False positive.")] /// # Hex (Bytes) to Signed. fn htoi(src: &[u8]) -> Option { <$unsigned>::htou(src).map(|n| n as Self) diff --git a/src/traits/inflect.rs b/src/traits/inflect.rs index 513e566..4a1dcb1 100644 --- a/src/traits/inflect.rs +++ b/src/traits/inflect.rs @@ -7,14 +7,18 @@ use crate::{ NiceU16, NiceU32, NiceU64, + NiceWrapper, }; -use std::num::{ - NonZeroU8, - NonZeroU16, - NonZeroU32, - NonZeroU64, - NonZeroUsize, - NonZeroU128, +use std::{ + fmt, + num::{ + NonZeroU8, + NonZeroU16, + NonZeroU32, + NonZeroU64, + NonZeroUsize, + NonZeroU128, + }, }; @@ -48,7 +52,7 @@ pub trait Inflection: Sized + Copy + PartialEq { /// This extends the `Inflection` trait for types which can be represented as /// one of the `NiceU*` types, and their signed equivalents (minus signs will /// be prepended as necessary), i.e. `i/u/NonZeroU 8–64`. -pub trait NiceInflection: Inflection { +pub trait NiceInflection: Inflection { /// # Inflect a String (Prefixed w/ Value) /// /// This is like [`Inflection::inflect`], but prefixes the output with a @@ -58,13 +62,151 @@ pub trait NiceInflection: Inflection { /// /// ``` /// use dactyl::traits::NiceInflection; + /// + /// // The return type implements fmt::Display, so you can chuck it + /// // straight into a formatter pattern like this: + /// assert_eq!( + /// format!("I have {}!", 3283.nice_inflect("book", "books")), + /// "I have 3,283 books!", + /// ); + /// + /// // Alternatively, you can save it to a variable to access the + /// // inner parts. + /// let nice = 3283.nice_inflect("book", "books"); + /// assert!(! nice.is_negative()); // Would be true for e.g. -5. /// assert_eq!( - /// 3283.nice_inflect("book", "books"), - /// "3,283 books" + /// nice.nice().as_str(), // Note: always positive. + /// "3,283", /// ); + /// assert_eq!(nice.unit(), "books"); + /// ``` + fn nice_inflect<'a>(self, singular: &'a str, plural: &'a str) -> NiceInflected<'a, S>; +} + + + +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +/// # Nice Inflection Wrapper. +/// +/// This struct serves as the return type for [`NiceInflection::nice_inflect`]. +/// It implements [`Display`](fmt::Display) so can be chucked straight into a +/// formatting pattern or converted to a string via `to_string()`. +/// +/// ## Examples +/// +/// ``` +/// use dactyl::traits::NiceInflection; +/// +/// assert_eq!( +/// format!( +/// "I have eaten {} and {}!", +/// 1001.nice_inflect("hotdog", "hotdogs"), +/// 1.nice_inflect("hamburger", "hamburger"), +/// ), +/// "I have eaten 1,001 hotdogs and 1 hamburger!", +/// ); +/// ``` +pub struct NiceInflected<'a, const S: usize> { + /// # Negative? + neg: bool, + + /// # The Number. + nice: NiceWrapper, + + /// # The Inflected Text. + unit: &'a str, +} + +impl fmt::Display for NiceInflected<'_, S> { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Add a minus sign to the start if negative. + if self.neg { f.write_str("-")?; } + + // Print the number. + f.write_str(self.nice.as_str())?; + + // Add a space. + f.write_str(" ")?; + + // And print the text value. + f.write_str(self.unit) + } +} + +impl NiceInflected<'_, S> { + /// # Length. + /// + /// Return the length of the string. + /// + /// ## Examples + /// + /// ``` + /// use dactyl::traits::NiceInflection; + /// + /// let dogs = 8_i32; + /// let nice_dogs = dogs.nice_inflect("dog", "dogs"); + /// assert_eq!(nice_dogs.len(), 6); // "8 dogs" + /// + /// let cats = -13_i32; + /// let nice_cats = cats.nice_inflect("cat", "cats"); + /// assert_eq!(nice_cats.len(), 8); // "-13 cats" + /// ``` + pub const fn len(&self) -> usize { + self.neg as usize + self.nice.len() + 1 + self.unit.len() + } +} + +impl NiceInflected<'_, S> { + /// Is Negative? + /// + /// Returns `true` if the original number was negative. + /// + /// ## Examples + /// + /// ``` + /// use dactyl::traits::NiceInflection; + /// + /// let dogs = 8_i32; + /// let nice_dogs = dogs.nice_inflect("dog", "dogs"); + /// assert!(! nice_dogs.is_negative()); + /// + /// let cats = -13_i32; + /// let nice_cats = cats.nice_inflect("cat", "cats"); + /// assert!(nice_cats.is_negative()); + /// ``` + pub const fn is_negative(&self) -> bool { self.neg } + + /// Nice Number. + /// + /// Returns the nicely-formatted number. + /// + /// Note: the nice value does not include the signing bit; if the raw value + /// was negative, you'll need to prepend an "-" before printing, etc. + /// + /// ## Examples + /// + /// ``` + /// use dactyl::traits::NiceInflection; + /// + /// let nice = 3000_u16.nice_inflect("dog", "dogs"); + /// assert_eq!(nice.nice().as_str(), "3,000"); + /// ``` + pub const fn nice(&self) -> NiceWrapper { self.nice } + + /// Inflected Unit. + /// + /// Returns the inflected unit. + /// + /// ## Examples + /// + /// ``` + /// use dactyl::traits::NiceInflection; + /// + /// let nice = 13_i32.nice_inflect("apple", "apples"); + /// assert_eq!(nice.unit(), "apples"); /// ``` - fn nice_inflect(self, singular: S, plural: S) -> String - where S: AsRef; + pub const fn unit(&self) -> &str { self.unit } } @@ -105,72 +247,66 @@ macro_rules! inflect { /// # Helper: Generate `Inflection` and `NiceInflection` impls. macro_rules! inflect_nice { // Unsigned. - ($ty:ty, $nice:ty) => ( - impl NiceInflection for $ty { + ($size:literal, $ty:ty, $nice:ty) => ( + impl NiceInflection<$size> for $ty { #[inline] /// # Inflect a String. - fn nice_inflect(self, singular: S, plural: S) -> String - where S: AsRef { - [ - <$nice>::from(self).as_str(), - " ", - self.inflect(singular.as_ref(), plural.as_ref()), - ].concat() + fn nice_inflect<'a>(self, singular: &'a str, plural: &'a str) -> NiceInflected<'a, $size> { + let nice = <$nice>::from(self); + let unit = self.inflect(singular, plural); + NiceInflected { neg: false, nice, unit } } } ); // Signed. - ($ty:ty, $nice:ty, $cast:ident) => ( - impl NiceInflection for $ty { + ($size:literal, $ty:ty, $nice:ty, $cast:ident) => ( + impl NiceInflection<$size> for $ty { #[inline] /// # Inflect a String. - fn nice_inflect(self, singular: S, plural: S) -> String - where S: AsRef { - [ - if self < 0 { "-" } else { "" }, - <$nice>::from(self.$cast()).as_str(), - " ", - self.inflect(singular.as_ref(), plural.as_ref()), - ].concat() + fn nice_inflect<'a>(self, singular: &'a str, plural: &'a str) -> NiceInflected<'a, $size> { + let neg = self < 0; + let nice = <$nice>::from(self.$cast()); + let unit = self.inflect(singular, plural); + NiceInflected { neg, nice, unit } } } ); // Unsigned, both impls. - ($ty:ty, $nice:ty, $one:literal) => ( + ($size:literal, $ty:ty, $nice:ty, $one:literal) => ( inflect!($ty, $one); - inflect_nice!($ty, $nice); + inflect_nice!($size, $ty, $nice); ); // Nonzero, both impls. - ($ty:ty, $nice:ty, $one:expr) => ( + ($size:literal, $ty:ty, $nice:ty, $one:expr) => ( inflect!($ty, $one); - inflect_nice!($ty, $nice); + inflect_nice!($size, $ty, $nice); ); // Signed, both impls. - ($ty:ty, $nice:ty, $one:literal, $cast:ident) => ( + ($size:literal, $ty:ty, $nice:ty, $one:literal, $cast:ident) => ( inflect!($ty, $one, $cast); - inflect_nice!($ty, $nice, $cast); + inflect_nice!($size, $ty, $nice, $cast); ); } -inflect_nice!(u8, NiceU8, 1); -inflect_nice!(u16, NiceU16, 1); -inflect_nice!(u32, NiceU32, 1); -inflect_nice!(u64, NiceU64, 1); -inflect_nice!(usize, NiceU64, 1); -inflect_nice!(NonZeroU8, NiceU8, Self::MIN); -inflect_nice!(NonZeroU16, NiceU16, Self::MIN); -inflect_nice!(NonZeroU32, NiceU32, Self::MIN); -inflect_nice!(NonZeroU64, NiceU64, Self::MIN); -inflect_nice!(NonZeroUsize, NiceU64, Self::MIN); -inflect_nice!(i8, NiceU8, 1, unsigned_abs); -inflect_nice!(i16, NiceU16, 1, unsigned_abs); -inflect_nice!(i32, NiceU32, 1, unsigned_abs); -inflect_nice!(i64, NiceU64, 1, unsigned_abs); -inflect_nice!(isize, NiceU64, 1, unsigned_abs); +inflect_nice!(3, u8, NiceU8, 1); +inflect_nice!(6, u16, NiceU16, 1); +inflect_nice!(13, u32, NiceU32, 1); +inflect_nice!(26, u64, NiceU64, 1); +inflect_nice!(26, usize, NiceU64, 1); +inflect_nice!(3, NonZeroU8, NiceU8, Self::MIN); +inflect_nice!(6, NonZeroU16, NiceU16, Self::MIN); +inflect_nice!(13, NonZeroU32, NiceU32, Self::MIN); +inflect_nice!(26, NonZeroU64, NiceU64, Self::MIN); +inflect_nice!(26, NonZeroUsize, NiceU64, Self::MIN); +inflect_nice!(3, i8, NiceU8, 1, unsigned_abs); +inflect_nice!(6, i16, NiceU16, 1, unsigned_abs); +inflect_nice!(13, i32, NiceU32, 1, unsigned_abs); +inflect_nice!(26, i64, NiceU64, 1, unsigned_abs); +inflect_nice!(26, isize, NiceU64, 1, unsigned_abs); // These aren't nice, but we can still do basic inflection. inflect!(u128, 1); @@ -216,7 +352,7 @@ mod tests { ($num:expr, $str:literal) => ( t_inflect!($num, $str); assert_eq!( - $num.nice_inflect("book", "books"), + $num.nice_inflect("book", "books").to_string(), format!(concat!("{} ", $str), $num.to_formatted_string(&Locale::en)), "{}.nice_inflect()", $num ); diff --git a/src/traits/intdiv.rs b/src/traits/intdiv.rs index b098653..412498d 100644 --- a/src/traits/intdiv.rs +++ b/src/traits/intdiv.rs @@ -2,6 +2,12 @@ # Dactyl: Integer Division */ +#![expect( + clippy::cast_lossless, + clippy::cast_precision_loss, + reason = "It is what it is.", +)] + /// # Integer Float Division. @@ -32,8 +38,6 @@ pub trait IntDivFloat: Copy { macro_rules! intdiv { ($($ty:ty),+) => ($( impl IntDivFloat for $ty { - #[allow(clippy::cast_lossless)] // It is what it is. - #[allow(clippy::cast_precision_loss)] // It is what it is. #[inline] /// # Integer to Float Division. /// @@ -73,7 +77,7 @@ mod test { use super::*; #[test] - #[allow(clippy::cognitive_complexity)] // It is what it is. + #[expect(clippy::cognitive_complexity, reason = "It is what it is.")] fn t_div_float() { macro_rules! t_div { ($($ty:ty),+) => ($( diff --git a/src/traits/saturating_from.rs b/src/traits/saturating_from.rs index 88dd369..d38453c 100644 --- a/src/traits/saturating_from.rs +++ b/src/traits/saturating_from.rs @@ -24,10 +24,13 @@ assert_eq!(u8::saturating_from(99_u64), 99_u8); ``` */ -#![allow(clippy::cast_lossless)] // We're doing a lot of this here. -#![allow(clippy::cast_possible_truncation)] // We're doing a lot of this here. -#![allow(clippy::cast_possible_wrap)] // We're doing a lot of this here. -#![allow(clippy::cast_sign_loss)] // We're doing a lot of this here. +#![expect( + clippy::cast_lossless, + clippy::cast_possible_truncation, + clippy::cast_possible_wrap, + clippy::cast_sign_loss, + reason = "We're doing a lot of this here.", +)] @@ -67,8 +70,11 @@ float!(f64, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); #[cfg(test)] -#[allow(clippy::cognitive_complexity)] // It is what it is. -#[allow(trivial_numeric_casts)] // It is what it is. +#[expect( + clippy::cognitive_complexity, + trivial_numeric_casts, + reason = "It is what it is.", +)] /// # Saturation Tests. /// /// There isn't a particularly good way to do this other than to walk through