@@ 9,6 9,7 @@ pub mod trie;
pub mod plugin;
pub mod command;
pub mod cache;
+pub mod num;
pub(crate) trait Ropey {
fn starts_with(&self, prefix: &str) -> bool;
@@ 0,0 1,185 @@
+macro_rules! ints {
+ ($mac:ident) => {
+ $mac!(u8);
+ $mac!(i8);
+ $mac!(u16);
+ $mac!(i16);
+ $mac!(u32);
+ $mac!(i32);
+ $mac!(u64);
+ $mac!(i64);
+ #[cfg(has_u128)]
+ $mac!(u128);
+ #[cfg(has_i128)]
+ $mac!(i128);
+ $mac!(usize);
+ $mac!(isize);
+ }
+}
+
+pub trait NumDigits {
+ type Output;
+
+ fn num_digits(&self) -> Self::Output;
+}
+
+macro_rules! impl_numdigits {
+ ($ty:ty) => {
+ impl NumDigits for $ty {
+ type Output = u8;
+
+ fn num_digits(&self) -> Self::Output {
+ let mut digits = 1;
+ let mut num = *self;
+ while num >= 10 {
+ num /= 10;
+ digits += 1;
+ }
+ digits
+ }
+ }
+ }
+}
+ints!(impl_numdigits);
+
+pub trait Digits: Sized {
+ fn digits(&self) -> DigitIter<Self>;
+}
+
+impl<T> Digits for T where T: Clone {
+ fn digits(&self) -> DigitIter<T> {
+ DigitIter(self.clone())
+ }
+}
+
+pub struct DigitIter<T>(T);
+
+impl<T> DigitIter<T> {
+ pub fn str(self) -> DigitStrIter<T> {
+ DigitStrIter(self)
+ }
+}
+
+macro_rules! impl_digititer {
+ ($ty:ty) => {
+ impl Iterator for DigitIter<$ty> {
+ type Item = u8;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.0 == 0 { return None }
+
+ let digit = self.0 % 10;
+ self.0 /= 10;
+
+ Some(digit as u8)
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ let num_digits = self.0.num_digits().into();
+ (num_digits, Some(num_digits))
+ }
+ }
+ }
+}
+ints!(impl_digititer);
+
+impl<T> ExactSizeIterator for DigitIter<T> where
+ T: NumDigits<Output=u8>,
+ DigitIter<T>: Iterator<Item=u8>
+{
+ fn len(&self) -> usize {
+ self.0.num_digits().into()
+ }
+}
+
+pub struct DigitStrIter<T>(DigitIter<T>);
+
+impl<T> Iterator for DigitStrIter<T> where DigitIter<T>: Iterator<Item=u8> {
+ type Item = &'static str;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.0.next().map(|digit| match &digit {
+ 0 => "0",
+ 1 => "1",
+ 2 => "2",
+ 3 => "3",
+ 4 => "4",
+ 5 => "5",
+ 6 => "6",
+ 7 => "7",
+ 8 => "8",
+ 9 => "9",
+ _ => unreachable!()
+ })
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.0.size_hint()
+ }
+}
+
+impl<T> ExactSizeIterator for DigitStrIter<T> where
+ T: NumDigits<Output=u8>,
+ DigitIter<T>: ExactSizeIterator,
+ DigitStrIter<T>: Iterator<Item=&'static str>
+{
+ fn len(&self) -> usize {
+ self.0.len()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn num_digits() {
+ assert_eq!(0u8.num_digits(), 1);
+ assert_eq!(1u8.num_digits(), 1);
+ assert_eq!(12i8.num_digits(), 2);
+ assert_eq!(123u16.num_digits(), 3);
+ assert_eq!(1234i16.num_digits(), 4);
+ assert_eq!(12345u32.num_digits(), 5);
+ assert_eq!(123456i32.num_digits(), 6);
+ assert_eq!(1234567u64.num_digits(), 7);
+ assert_eq!(12345678i64.num_digits(), 8);
+ #[cfg(has_u128)]
+ assert_eq!(123456789u128.num_digits(), 9);
+ #[cfg(has_i128)]
+ assert_eq!(1234567890i128.num_digits(), 10);
+ }
+
+ #[test]
+ fn digits() {
+ macro_rules! test {
+ ($ty:ty) => {
+ let num: $ty = 123;
+ let mut iter = num.digits();
+ assert_eq!(iter.len(), 3);
+ assert_eq!(iter.size_hint(), (3, Some(3)));
+ assert_eq!(iter.next(), Some(3));
+ assert_eq!(iter.next(), Some(2));
+ assert_eq!(iter.next(), Some(1));
+ assert_eq!(iter.next(), None);
+ }
+ }
+ ints!(test);
+ }
+
+ #[test]
+ fn digits_str() {
+ macro_rules! test {
+ ($ty:ty) => {
+ let num: $ty = 123;
+ let mut iter = num.digits().str();
+ assert_eq!(iter.len(), 3);
+ assert_eq!(iter.size_hint(), (3, Some(3)));
+ assert_eq!(iter.next(), Some("3"));
+ assert_eq!(iter.next(), Some("2"));
+ assert_eq!(iter.next(), Some("1"));
+ assert_eq!(iter.next(), None);
+ }
+ }
+ ints!(test);
+ }
+}