#[cfg(feature = "serde")]
mod serde_impl;
use std::fmt;
use std::ops::{Add, Sub};
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, Default)]
#[repr(transparent)]
pub struct Osize(usize);
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, Default)]
#[repr(transparent)]
pub struct O128(u128);
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, Default)]
#[repr(transparent)]
pub struct O64(u64);
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, Default)]
#[repr(transparent)]
pub struct O32(u32);
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, Default)]
#[repr(transparent)]
pub struct O16(u16);
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, Default)]
#[repr(transparent)]
pub struct O8(u8);
pub trait Ordinal:
Sized
+ Eq
+ PartialEq
+ Ord
+ PartialOrd
+ std::hash::Hash
+ Clone
+ Copy
+ Default
+ fmt::Display
+ fmt::Debug
{
type IntegerType: Copy + fmt::Display;
fn first() -> Self;
fn next(self) -> Self;
fn into0(self) -> Self::IntegerType;
fn into1(self) -> Self::IntegerType;
fn try_from0(t: Self::IntegerType) -> Option<Self>;
fn try_from1(t: Self::IntegerType) -> Option<Self>;
fn from0(t: Self::IntegerType) -> Self {
Self::try_from0(t).unwrap_or_else(|| panic!("value {} is too big for this ordinal type", t))
}
fn from1(t: Self::IntegerType) -> Self {
Self::try_from1(t).expect("0 is not a valid 1-based ordinal.")
}
}
macro_rules! impl_ordinal {
($t:ident, $int:ident) => {
impl Ordinal for $t {
type IntegerType = $int;
fn first() -> Self {
Self(0)
}
fn next(self) -> Self {
Self::from0(self.0 + 1)
}
fn into0(self) -> Self::IntegerType {
self.0
}
fn into1(self) -> Self::IntegerType {
self.0 + 1
}
fn try_from0(t: Self::IntegerType) -> Option<Self> {
match t {
$int::MAX => None,
_ => Some($t(t)),
}
}
fn try_from1(t: Self::IntegerType) -> Option<Self> {
t.checked_sub(1).map($t)
}
}
impl fmt::Debug for $t {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 + 1 {
1 => write!(f, "first"),
2 => write!(f, "second"),
3 => write!(f, "third"),
n => {
let two_digits = n % 100;
let digit = two_digits % 10;
if digit == 1 && two_digits != 11 {
write!(f, "{}st", n)
} else if digit == 2 && two_digits != 12 {
write!(f, "{}nd", n)
} else if digit == 3 && two_digits != 13 {
write!(f, "{}rd", n)
} else {
write!(f, "{}th", n)
}
}
}
}
}
impl fmt::Display for $t {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl Add<$int> for $t {
type Output = $t;
fn add(self, rhs: $int) -> Self::Output {
Self::from0(self.0 + rhs)
}
}
impl Sub<$int> for $t {
type Output = $t;
fn sub(self, rhs: $int) -> Self::Output {
Self::from0(self.0 - rhs)
}
}
impl Sub<$t> for $t {
type Output = $int;
fn sub(self, rhs: $t) -> Self::Output {
self.0 - rhs.0
}
}
};
}
impl_ordinal!(Osize, usize);
impl_ordinal!(O128, u128);
impl_ordinal!(O64, u64);
impl_ordinal!(O32, u32);
impl_ordinal!(O16, u16);
impl_ordinal!(O8, u8);
pub fn ordinal1<O: Ordinal>(n: O::IntegerType) -> O {
O::from1(n)
}
pub fn ordinal0<O: Ordinal>(n: O::IntegerType) -> O {
O::from0(n)
}
#[macro_export]
macro_rules! ordinal {
(first $($ty:ident)?) => {
$crate::ordinal1 $(::<$crate::$ty>)? (1)
};
(second $($ty:ident)?) => {
$crate::ordinal1 $(::<$crate::$ty>)? (2)
};
(third $($ty:ident)?) => {
$crate::ordinal1 $(::<$crate::$ty>)? (3)
};
($n:literal $(-)? $suffix:ident $($ty:ident)?) => {
$crate::ordinal1 $(::<$crate::$ty>)? ($n)
};
($n:literal . $($ty:ident)?) => {
$crate::ordinal1 $(::<$crate::$ty>)? ($n)
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn first_from0() {
let first_o_from0 = Osize::from0(0);
assert_eq!(&first_o_from0.to_string(), "first");
}
#[test]
fn first_from1() {
let first_o_from1 = Osize::from1(1);
assert_eq!(&first_o_from1.to_string(), "first");
}
#[test]
fn second_from0() {
let second_o_from0 = Osize::from0(1);
assert_eq!(&second_o_from0.to_string(), "second");
}
#[test]
fn second_from1() {
let second_o_from1 = Osize::from1(2);
assert_eq!(&second_o_from1.to_string(), "second");
}
#[test]
fn third_from0() {
let third_o_from0 = Osize::from0(2);
assert_eq!(&third_o_from0.to_string(), "third");
}
#[test]
fn third_from1() {
let third_o_from1 = Osize::from1(3);
assert_eq!(&third_o_from1.to_string(), "third");
}
#[test]
fn fourth_from0() {
let fourth_o_from0 = Osize::from0(3);
assert_eq!(&fourth_o_from0.to_string(), "4th");
}
#[test]
fn fourth_from1() {
let fourth_o_from1 = Osize::from1(4);
assert_eq!(&fourth_o_from1.to_string(), "4th");
}
}