1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
/*! Ordinal number types
Ordinal numbers (_first, second, third, ..._) are usually represented
as 0-based or 1-based integers. In English and most other natural
languages, they're represented as 1-based numbers:
first = 1st, second = 2nd, third = 3rd and so on.
However, most programming languages are zero-based, i.e. when getting
the first element in array or list, the index is 0. This is also true for Rust.
# Usage
To make working with ordinal numbers more explicit and less error-prone,
this library provides ordinal number types that can be converted to/from
cardinal numbers while specifying if it is 0- or 1-based:
```rust
use num_ordinal::{Ordinal, Osize};
// Osize is an ordinal usize
let o = Osize::from0(3);
assert_eq!(&o.to_string(), "4th");
let o = Osize::from1(3);
assert_eq!(&o.to_string(), "third");
```
There are also two convenience functions to create ordinal numbers
when the return type can be inferred:
```rust
use num_ordinal::{Osize, ordinal0, ordinal1};
// Osize is an ordinal usize
let o: Osize = ordinal0(3);
assert_eq!(&o.to_string(), "4th");
let o: Osize = ordinal1(3);
assert_eq!(&o.to_string(), "third");
```
And [a macro](ordinal):
```rust
use num_ordinal::{O32, ordinal};
// type is inferred:
let o: O32 = ordinal!(4-th);
// type can also be specified:
let o = ordinal!(4-th O32);
```
# Implemented traits
Ordinal numbers implement a number of traits, so they can be
compared, hashed, copied and formatted. Also, you can add or
subtract an integer from an ordinal number:
```rust
use num_ordinal::ordinal;
assert_eq!(ordinal!(5-th O32) - 3, ordinal!(second O32));
```
Subtracting an ordinal from an ordinal produces an integer:
```rust
use num_ordinal::ordinal;
assert_eq!(ordinal!(5-th O32) - ordinal!(second O32), 3);
```
The default value is _first_.
# Features
* `serde`: Implement `Serialize` and `Deserialize` for ordinals
# License
MIT
*/
#[cfg(feature = "serde")]
mod serde_impl;
use std::fmt;
use std::ops::{Add, Sub};
/// [Ordinal] number represented by [usize]
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, Default)]
#[repr(transparent)]
pub struct Osize(usize);
/// [Ordinal] number represented by [u128]
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, Default)]
#[repr(transparent)]
pub struct O128(u128);
/// [Ordinal] number represented by [u64]
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, Default)]
#[repr(transparent)]
pub struct O64(u64);
/// [Ordinal] number represented by [u32]
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, Default)]
#[repr(transparent)]
pub struct O32(u32);
/// [Ordinal] number represented by [u16]
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, Default)]
#[repr(transparent)]
pub struct O16(u16);
/// [Ordinal] number represented by [u8]
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, Default)]
#[repr(transparent)]
pub struct O8(u8);
/// An ordinal number type
///
/// See the [module-level documentation](index.html) for more.
pub trait Ordinal:
Sized
+ Eq
+ PartialEq
+ Ord
+ PartialOrd
+ std::hash::Hash
+ Clone
+ Copy
+ Default
+ fmt::Display
+ fmt::Debug
{
/// This type by which this ordinal type is represented
type IntegerType: Copy + fmt::Display;
/// The first ordinal number
fn first() -> Self;
/// Computes the ordinal number that comes after this one
fn next(self) -> Self;
/// Returns the equivalent integer assuming the ordinal number is 0-based
fn into0(self) -> Self::IntegerType;
/// Returns the equivalent integer assuming the ordinal number is 1-based
fn into1(self) -> Self::IntegerType;
/// Tries to convert an integer to a 0-based ordinal number.
///
/// It returns [None] if the provided number is the highest number of that integer type.
/// This fails because that number can't be incremented by 1.
fn try_from0(t: Self::IntegerType) -> Option<Self>;
/// Tries to convert an integer to a 1-based ordinal number.
///
/// It returns [None] if the provided number is 0.
fn try_from1(t: Self::IntegerType) -> Option<Self>;
/// Converts an integer to a 0-based ordinal number.
///
/// ### Panics
///
/// Panics if the provided number is the highest number of that integer type.
/// This fails because that number can't be incremented by 1.
fn from0(t: Self::IntegerType) -> Self {
Self::try_from0(t).unwrap_or_else(|| panic!("value {} is too big for this ordinal type", t))
}
/// Converts an integer to a 1-based ordinal number.
///
/// ### Panics
///
/// Panics if the provided number is 0.
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 {
0 => write!(f, "first"),
1 => 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);
/// Creates a 1-based ordinal number. For example, `ordinal1(4)` is the 4th ordinal number.
pub fn ordinal1<O: Ordinal>(n: O::IntegerType) -> O {
O::from1(n)
}
/// Creates a 0-based ordinal number. For example, `ordinal0(4)` is the 5th ordinal number.
pub fn ordinal0<O: Ordinal>(n: O::IntegerType) -> O {
O::from0(n)
}
/// Creates a 1-based ordinal number. Examples:
///
/// ```
/// use num_ordinal::{O32, ordinal};
///
/// let mut o: O32 = ordinal!(first);
/// o = ordinal!(second);
/// o = ordinal!(third);
///
/// // Other numbers must use the following syntax:
/// o = ordinal!(4-th);
/// // the dash can be omitted, but then a space is required to make the Rust parser happy:
/// o = ordinal!(4 th);
/// // alternatively, a dot can be written after the number:
/// o = ordinal!(4 .);
///
/// // When necessary, the type can be ascribed:
/// let o = ordinal!(5-th O32);
/// ```
///
/// Note that only `first`, `second` and `third` can be written as a full word:
///
/// ```compile_fail
/// use num_ordinal::{O32, ordinal};
///
/// // doesn't compile!
/// let _: O32 = ordinal!(fifth);
/// ```
#[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)
};
}