#![cfg_attr(not(test), no_std)]
#![doc(html_root_url = "https://docs.rs/numid")]
#![warn(missing_docs)]
#[doc(hidden)]
pub extern crate const_fn_assert as _const_fn_assert;
#[doc(hidden)]
pub extern crate core as _core;
#[macro_export]
macro_rules! numid {
($(#[$attr:meta])* $vis:vis struct $name:ident) => {
numid!{$(#[$attr])* @CloneIsClone $vis struct $name(u64) -> 0 }
};
($(#[$attr:meta])* $vis:vis struct $name:ident -> $init_val:expr) => {
numid!{$(#[$attr])* @CloneIsClone $vis struct $name(u64) -> $init_val }
};
($(#[$attr:meta])* $vis:vis struct $name:ident($ty:ty)) => {
numid!{$(#[$attr])* @CloneIsClone $vis struct $name($ty) -> 0 }
};
($(#[$attr:meta])* $vis:vis struct $name:ident($ty:ty) -> $init_val:expr) => {
numid!{$(#[$attr])* @CloneIsClone $vis struct $name($ty) -> $init_val }
};
($(#[$attr:meta])* $(@$mattr:ident)+ $vis:vis struct $name:ident) => {
numid!{$(#[$attr])* $(@$mattr)+ $vis struct $name(u64) -> 0 }
};
($(#[$attr:meta])* $(@$mattr:ident)+ $vis:vis struct $name:ident -> $init_val:expr) => {
numid!{$(#[$attr])* $(@$mattr)+ $vis struct $name(u64) -> $init_val }
};
($(#[$attr:meta])* $(@$mattr:ident)+ $vis:vis struct $name:ident($ty:ty)) => {
numid!{$(#[$attr])* $(@$mattr)+ $vis struct $name($ty) -> 0 }
};
($(#[$attr:meta])* $(@$mattr:ident)* $vis:vis struct $name:ident($ty:ty) -> $init_val:expr) => {
#[warn(non_camel_case_types)]
#[warn(dead_code)] #[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
$(#[$attr])*
$vis struct $name($ty);
impl $name {
pub const INITIAL_VALUE: $ty = $init_val;
#[doc(hidden)]
#[inline]
unsafe fn __get_static_mut() -> &'static mut $ty {
static mut CURRENT_VALUE: $ty = $name::INITIAL_VALUE;
&mut CURRENT_VALUE
}
#[allow(dead_code)]
#[inline]
pub fn new() -> $name {
$name(unsafe {
let v = $name::__get_static_mut();
*v += 1;
*v
})
}
#[allow(dead_code)]
#[inline]
pub const fn value(self) -> $ty {
self.0
}
#[allow(dead_code)]
#[inline]
pub fn current_value() -> $ty {
unsafe {
*$name::__get_static_mut()
}
}
#[allow(dead_code)]
#[inline]
pub const fn initial_value() -> $ty {
$name::INITIAL_VALUE
}
#[allow(dead_code)]
pub fn replace_current_value(value: $ty) -> bool {
let cond = value > $name::current_value();
if cond {
unsafe {
let v = $name::__get_static_mut();
*v = value;
}
}
cond
}
#[allow(dead_code)]
#[inline]
pub fn create_maybe(value: $ty) -> Option<$name> {
if $name::replace_current_value(value) {
Some($name(value))
} else {
None
}
}
#[allow(dead_code)]
#[inline]
pub fn create_lower(value: $ty) -> $name {
assert!(value <= $name::initial_value());
$name(value)
}
#[allow(dead_code)]
#[inline]
pub const fn const_create_lower(value: $ty) -> $name {
$crate::_const_fn_assert::cfn_assert!(value <= $name::initial_value());
$name(value)
}
#[allow(dead_code)]
#[inline]
pub fn reproduce(self) -> $name {
if self.0 > $name::initial_value() {
$name::new()
} else {
self
}
}
}
impl Default for $name {
#[inline]
fn default() -> $name {
$name::new()
}
}
$crate::__macro_attr_numid!($name $($mattr)*);
$crate::__fmt_impl_numid!($name : Binary, Octal, LowerHex, UpperHex);
$crate::__display_numid!($name);
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! __macro_attr_numid {
($name:ident) => {};
($name:ident CloneIsClone $($stack:ident)*) => {
impl Clone for $name {
#[inline]
fn clone(&self) -> $name {
*self
}
}
impl Copy for $name { }
$crate::__macro_attr_numid!($name $($stack)*);
};
($name:ident CloneIsNew $($stack:ident)*) => {
impl Clone for $name {
#[inline]
fn clone(&self) -> $name {
$name::new()
}
}
impl Copy for $name { }
$crate::__macro_attr_numid!($name $($stack)*);
};
($name:ident CloneIsReproduce $($stack:ident)*) => {
impl Clone for $name {
#[inline]
fn clone(&self) -> $name {
self.reproduce()
}
}
impl Copy for $name { }
$crate::__macro_attr_numid!($name $($stack)*);
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! __fmt_impl_numid {
($name:ident : $($trait:ident),+) => {
$(
impl $crate::_core::fmt::$trait for $name {
fn fmt(&self, f: &mut $crate::_core::fmt::Formatter<'_>) -> $crate::_core::fmt::Result {
$crate::_core::fmt::$trait::fmt(&self.0, f)
}
}
)+
}
}
#[cfg(feature = "display")]
#[macro_export]
#[doc(hidden)]
macro_rules! __display_numid {
($name:ident) => {
$crate::__fmt_impl_numid!($name: Display);
};
}
#[cfg(not(feature = "display"))]
#[macro_export]
#[doc(hidden)]
macro_rules! __display_numid {
($name:ident) => {};
}
#[cfg(feature = "example")]
pub mod example;
#[cfg(all(test, not(feature = "example")))]
mod example;
#[cfg(test)]
mod tests {
numid!(struct Id);
numid!(struct IdWithInitVal -> 100);
numid!(struct UnusedId(u32) -> 10);
#[test]
fn tests_id_used() {
assert_eq!(Id::initial_value(), 0);
assert_eq!(Id::current_value(), Id::initial_value());
let id0 = Id::new();
assert_eq!(Id::current_value(), 1);
assert_eq!(id0.value(), Id::current_value());
let id1 = Id::new();
assert_eq!(Id::current_value(), 2);
assert_eq!(id1.value(), Id::current_value());
assert_ne!(id0, id1);
assert!(id1 > id0);
let id2 = Id::default();
assert_eq!(Id::current_value(), 3);
assert_eq!(id2.value(), Id::current_value());
assert!(id2 > id1);
assert!(Id::replace_current_value(10));
assert_eq!(Id::current_value(), 10);
assert_eq!(Id::replace_current_value(1), false);
assert_eq!(Id::current_value(), 10);
let id3 = Id::new();
assert_eq!(id3.value(), 11);
}
#[test]
fn tests_id_with_init_val_used() {
assert_eq!(IdWithInitVal::initial_value(), 100);
assert_eq!(
IdWithInitVal::current_value(),
IdWithInitVal::initial_value()
);
let id0 = IdWithInitVal::new();
assert_eq!(IdWithInitVal::current_value(), 101);
assert_eq!(id0.value(), IdWithInitVal::current_value());
let id1 = IdWithInitVal::new();
assert_eq!(IdWithInitVal::current_value(), 102);
assert_eq!(id1.value(), IdWithInitVal::current_value());
assert!(id1 > id0);
assert!(IdWithInitVal::replace_current_value(150));
assert_eq!(IdWithInitVal::current_value(), 150);
assert_eq!(IdWithInitVal::replace_current_value(1), false);
assert_eq!(IdWithInitVal::current_value(), 150);
let id2 = IdWithInitVal::new();
assert_eq!(id2.value(), 151);
}
#[test]
fn tests_create_lower() {
let id = Id::create_lower(0);
assert_eq!(id.value(), 0);
let _ = IdWithInitVal::create_lower(0);
let _ = IdWithInitVal::create_lower(50);
let _ = IdWithInitVal::create_lower(100);
let _ = UnusedId::create_lower(5);
}
#[test]
#[should_panic]
fn tests_create_lower_fail() {
let _ = IdWithInitVal::create_lower(101);
}
#[test]
fn test_const_create_lower() {
const C: Id = Id::const_create_lower(0);
assert_eq!(C.value(), 0);
let _ = IdWithInitVal::const_create_lower(0);
const _C0: IdWithInitVal = IdWithInitVal::const_create_lower(50);
const _C1: IdWithInitVal = IdWithInitVal::const_create_lower(100);
const _C2: UnusedId = UnusedId::const_create_lower(5);
}
#[test]
#[should_panic]
fn tests_const_create_lower_fail() {
let _ = IdWithInitVal::const_create_lower(101);
}
#[test]
fn tests_create_maybe() {
numid!(struct IdMaybe -> 1);
assert_eq!(IdMaybe::create_maybe(5).unwrap().value(), 5);
assert_eq!(IdMaybe::current_value(), 5);
assert_eq!(IdMaybe::create_maybe(3), None);
assert_eq!(IdMaybe::current_value(), 5);
assert_eq!(IdMaybe::create_maybe(5), None);
assert_eq!(IdMaybe::create_maybe(8).unwrap().value(), 8);
}
#[test]
fn tests_reproduce() {
numid!(struct Id -> 10);
let id0 = Id::new();
let id1 = Id::create_lower(5);
let id2 = Id::create_lower(10);
let id3 = id0.reproduce();
assert!(id0 < id3);
assert_eq!(id1, id1.reproduce());
assert_eq!(id2, id2.reproduce());
assert!(id3 < id0.reproduce());
}
#[test]
fn test_debug() {
numid!(struct IdDebug);
let id = IdDebug::new();
assert_eq!(format!("{:?}", id), "IdDebug(1)");
numid!(struct IdDebugHexa -> 0x1b3d - 1);
let idh = IdDebugHexa::new();
assert_eq!(format!("{:x?}", idh), "IdDebugHexa(1b3d)");
assert_eq!(format!("{:X?}", idh), "IdDebugHexa(1B3D)");
}
macro_rules! test_fmt {
($func:ident, $fmt:tt, $fmt2:tt, $value:expr, $fmt_repr:expr, $repr:expr) => {
#[test]
fn $func() {
numid!(struct Test -> $value - 1);
let id = Test::new();
assert_eq!(format!($fmt, id), $repr);
assert_eq!(format!($fmt2, id), concat!($fmt_repr, $repr));
}
};
}
test_fmt!(test_binary, "{:b}", "{:#b}", 0b1010, "0b", "1010");
test_fmt!(test_octal, "{:o}", "{:#o}", 0o7706, "0o", "7706");
test_fmt!(test_lowerhex, "{:x}", "{:#x}", 0xEFFF, "0x", "efff");
test_fmt!(test_upperhex, "{:X}", "{:#X}", 0xEFFF, "0x", "EFFF");
#[cfg(feature = "display")]
#[test]
fn test_display() {
numid!(struct IdDisplay);
let id = IdDisplay::new();
assert_eq!(format!("{}", id), "1");
}
mod submodule {
numid!(pub struct PublicId);
numid!(struct PrivateId);
#[test]
fn test_private() {
let _ = PrivateId::new();
}
}
#[test]
fn test_public() {
let _ = submodule::PublicId::new();
}
#[test]
fn test_pub_crate() {
mod module {
numid!(pub (crate) struct Test -> 4);
}
assert_eq!(module::Test::current_value(), 4);
}
#[test]
fn test_pub_in_module() {
mod module {
mod submodule {
numid!(
pub (in super) struct Test -> 7
);
}
mod test {
pub(super) fn value() -> u64 {
super::submodule::Test::current_value()
}
}
pub fn value() -> u64 {
test::value()
}
}
assert_eq!(module::value(), 7);
}
#[test]
fn test_clone_is_clone() {
numid!(@CloneIsClone struct Test);
let t = Test::new();
assert_eq!(t, t.clone());
let tt = Test::create_lower(0);
assert_eq!(tt, tt.clone());
}
#[test]
fn test_clone_is_new() {
numid!(@CloneIsNew struct Test);
let t = Test::new();
assert_ne!(t, t.clone());
let tt = Test::create_lower(0);
assert_ne!(tt, tt.clone());
}
#[test]
fn test_clone_is_reproduce() {
numid!(@CloneIsReproduce struct Test);
let t = Test::new();
assert_ne!(t, t.clone());
let tt = Test::create_lower(0);
assert_eq!(tt, tt.clone());
}
}