#![cfg_attr(
feature = "alloc",
doc = r##"
Format a number as an ordinal, allocating a new `String`:
```
use ordinal::ToOrdinal as _;
assert_eq!(12.to_ordinal_string(), "12th");
```
Get a number representing an ordinal you can use with comparisons and formatting.
```
use ordinal::ToOrdinal as _;
let n = 12.to_ordinal();
assert_eq!(*n, 12);
assert_eq!(format!("{n}"), "12th");
```
"##
)]
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
#[allow(unused_imports)]
#[cfg(feature = "alloc")]
use alloc::{
format,
string::{String, ToString as _},
};
use core::fmt;
#[cfg(feature = "alloc")]
mod number {
use super::*;
use core::ops::Deref;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Ordinal<T: ToOrdinal>(pub T);
impl<T: ToOrdinal> Ordinal<T> {
pub fn suffix(&self) -> &'static str {
self.0.suffix()
}
}
impl<T: ToOrdinal> Deref for Ordinal<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: ToOrdinal> fmt::Display for Ordinal<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", self.0, self.0.suffix())
}
}
impl<T: ToOrdinal> From<T> for Ordinal<T> {
fn from(value: T) -> Self {
Ordinal(value)
}
}
#[test]
fn test_number() {
const TWO: Ordinal<i32> = Ordinal(2);
let twelve = Ordinal::from(12);
assert!(TWO < twelve);
assert_eq!(*twelve, 12);
assert_eq!(twelve.to_string(), String::from("12th"));
}
}
#[cfg(feature = "alloc")]
pub use number::Ordinal;
pub trait ToOrdinal: fmt::Display + Copy {
#[cfg(feature = "alloc")]
fn to_ordinal(self) -> Ordinal<Self> {
Ordinal(self)
}
#[cfg(feature = "alloc")]
fn to_ordinal_string(self) -> String {
format!("{}{}", self, self.suffix())
}
fn suffix(self) -> &'static str;
}
macro_rules! impl_ordinal {
($($t:ty)*) => { $(
impl $crate::ToOrdinal for $t {
fn suffix(self) -> &'static str {
let n = Abs::abs(self);
let n = (n % 20) as u8;
if (11..=13).contains(&n) {
return "th";
}
match (n % 10) {
1 => "st",
2 => "nd",
3 => "rd",
_ => "th",
}
}
}
)* }
}
impl_ordinal!(u8 u16 u32 u64 u128 usize);
impl_ordinal!(i8 i16 i32 i64 i128 isize);
trait Abs<T> {
fn abs(self) -> T;
}
macro_rules! impl_abs {
(signed $($t:ty)*) => { $(
impl $crate::Abs<$t> for $t {
fn abs(self) -> $t {
self.abs()
}
}
)* };
(unsigned $($t:ty)*) => { $(
impl $crate::Abs<$t> for $t {
fn abs(self) -> $t {
self
}
}
)* };
}
impl_abs!(unsigned u8 u16 u32 u64 u128 usize);
impl_abs!(signed i8 i16 i32 i64 i128 isize);
#[cfg(feature = "alloc")]
#[test]
fn test_fmt() {
assert_eq!(0u8.to_ordinal_string(), "0th");
assert_eq!(1u16.to_ordinal_string(), "1st");
assert_eq!(2u32.to_ordinal_string(), "2nd");
assert_eq!(3u64.to_ordinal_string(), "3rd");
assert_eq!(4u128.to_ordinal_string(), "4th");
assert_eq!(5usize.to_ordinal_string(), "5th");
assert_eq!(6i8.to_ordinal_string(), "6th");
assert_eq!(7i16.to_ordinal_string(), "7th");
assert_eq!(8i32.to_ordinal_string(), "8th");
assert_eq!(9i64.to_ordinal_string(), "9th");
assert_eq!(10i128.to_ordinal_string(), "10th");
assert_eq!(11isize.to_ordinal_string(), "11th");
assert_eq!((-0i8).to_ordinal_string(), "0th");
assert_eq!((-1i16).to_ordinal_string(), "-1st");
assert_eq!((-2i32).to_ordinal_string(), "-2nd");
assert_eq!((-3i64).to_ordinal_string(), "-3rd");
assert_eq!((-4i128).to_ordinal_string(), "-4th");
assert_eq!((-5isize).to_ordinal_string(), "-5th");
assert_eq!((-6i8).to_ordinal_string(), "-6th");
assert_eq!((-7i16).to_ordinal_string(), "-7th");
assert_eq!((-8i32).to_ordinal_string(), "-8th");
assert_eq!((-9i64).to_ordinal_string(), "-9th");
assert_eq!((-10i128).to_ordinal_string(), "-10th");
assert_eq!((-11isize).to_ordinal_string(), "-11th");
assert_eq!(19u8.to_ordinal_string(), "19th");
assert_eq!(20u8.to_ordinal_string(), "20th");
assert_eq!(21u8.to_ordinal_string(), "21st");
assert_eq!(22u8.to_ordinal_string(), "22nd");
assert_eq!(23u8.to_ordinal_string(), "23rd");
assert_eq!(24u8.to_ordinal_string(), "24th");
assert_eq!(100u8.to_ordinal_string(), "100th");
assert_eq!(101u8.to_ordinal_string(), "101st");
assert_eq!(111u8.to_ordinal_string(), "111th");
assert_eq!(112u8.to_ordinal_string(), "112th");
assert_eq!(1001u32.to_ordinal_string(), "1001st");
assert_eq!(1002u32.to_ordinal_string(), "1002nd");
assert_eq!(1003u32.to_ordinal_string(), "1003rd");
assert_eq!(1004u32.to_ordinal_string(), "1004th");
assert_eq!(10001001u128.to_ordinal_string(), "10001001st");
assert_eq!(10001002u128.to_ordinal_string(), "10001002nd");
assert_eq!(10001003u128.to_ordinal_string(), "10001003rd");
assert_eq!(10001004u128.to_ordinal_string(), "10001004th");
assert_eq!(10001111u128.to_ordinal_string(), "10001111th");
assert_eq!(10001111u128.to_ordinal_string(), "10001111th");
assert_eq!(10001111u128.to_ordinal_string(), "10001111th");
}
#[test]
fn test_suffix() {
assert_eq!(0u8.suffix(), "th");
assert_eq!(1u16.suffix(), "st");
assert_eq!(2u32.suffix(), "nd");
assert_eq!(3u64.suffix(), "rd");
assert_eq!(4u128.suffix(), "th");
assert_eq!(5usize.suffix(), "th");
assert_eq!(6i8.suffix(), "th");
assert_eq!(7i16.suffix(), "th");
assert_eq!(8i32.suffix(), "th");
assert_eq!(9i64.suffix(), "th");
assert_eq!(10i128.suffix(), "th");
assert_eq!(11isize.suffix(), "th");
assert_eq!((-0i8).suffix(), "th");
assert_eq!((-1i16).suffix(), "st");
assert_eq!((-2i32).suffix(), "nd");
assert_eq!((-3i64).suffix(), "rd");
assert_eq!((-4i128).suffix(), "th");
assert_eq!((-5isize).suffix(), "th");
assert_eq!((-6i8).suffix(), "th");
assert_eq!((-7i16).suffix(), "th");
assert_eq!((-8i32).suffix(), "th");
assert_eq!((-9i64).suffix(), "th");
assert_eq!((-10i128).suffix(), "th");
assert_eq!((-11isize).suffix(), "th");
assert_eq!(19u8.suffix(), "th");
assert_eq!(20u8.suffix(), "th");
assert_eq!(21u8.suffix(), "st");
assert_eq!(22u8.suffix(), "nd");
assert_eq!(23u8.suffix(), "rd");
assert_eq!(24u8.suffix(), "th");
assert_eq!(100u8.suffix(), "th");
assert_eq!(101u8.suffix(), "st");
assert_eq!(111u8.suffix(), "th");
assert_eq!(112u8.suffix(), "th");
assert_eq!(1001u32.suffix(), "st");
assert_eq!(1002u32.suffix(), "nd");
assert_eq!(1003u32.suffix(), "rd");
assert_eq!(1004u32.suffix(), "th");
assert_eq!(10001001u128.suffix(), "st");
assert_eq!(10001002u128.suffix(), "nd");
assert_eq!(10001003u128.suffix(), "rd");
assert_eq!(10001004u128.suffix(), "th");
assert_eq!(10001111u128.suffix(), "th");
assert_eq!(10001111u128.suffix(), "th");
assert_eq!(10001111u128.suffix(), "th");
}
#[cfg(all(doctest, feature = "std"))]
#[doc = include_str!("../README.md")]
struct ReadMe;