1use crate::*;
2
3#[derive(PartialEq, Eq, Clone, Copy, Hash)]
20pub struct PaddedNumber<const A: u8 = 1, const B: u8 = { u8::MAX }> {
21 pub(crate) leading_zeros: u8,
22 pub(crate) number: u64,
23}
24
25impl<const A: u8, const B: u8> PaddedNumber<A, B> {
26 #[doc(hidden)]
27 pub const unsafe fn new_unchecked(leading_zeros: u8, number: u64) -> Self {
28 Self { leading_zeros, number }
29 }
30
31 pub const fn try_new(str: &str) -> Result<Self, ParsePaddedNumberError> {
33 let (leading_zeros, remaining_number) = konst::try_!(padded_number_internal::parse(A, B, str));
34
35 Ok(Self { leading_zeros, number: remaining_number })
36 }
37
38 pub const fn len(&self) -> u8 {
46 self.leading_zeros + self.number_len()
47 }
48
49 pub(crate) const fn number_len(&self) -> u8 {
51 let number = self.number;
52
53 if number == 0 {
54 return 0;
55 }
56
57 let mut number_length = 1;
58 let mut remaining_number = number;
59
60 while remaining_number >= 10 {
61 number_length += 1;
62 remaining_number /= 10;
63 }
64
65 number_length
66 }
67
68 pub const fn is_empty(&self) -> bool {
75 self.leading_zeros == 0 && self.number == 0
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use crate::tests::mock_from_str;
83
84 #[test]
85 fn new_with_leading_zeros() {
86 let number = mock_from_str::<1, 3>("001");
87 assert_eq!(2, number.leading_zeros);
88 }
89
90 #[test]
91 fn new_with_leading_zeros_only() {
92 let number = mock_from_str::<1, 3>("000");
93 let expected = PaddedNumber { leading_zeros: 3, number: 0 };
94 assert_eq!(expected, number)
95 }
96
97 #[test]
98 fn new_with_empty_str() {
99 let number = mock_from_str::<0, 0>("");
100 assert!(number.is_empty());
101 }
102
103 #[test]
104 fn too_long_error() {
105 let invalid_number = "123";
106
107 let actual_err = invalid_number.parse::<PaddedNumber<1, 2>>().unwrap_err();
108
109 assert_eq!(ParsePaddedNumberError::TooLong(2, 3), actual_err);
110 }
111
112 #[test]
113 fn too_short_error() {
114 let invalid_number = "";
115
116 let actual_err = invalid_number.parse::<PaddedNumber<1, 2>>().unwrap_err();
117
118 assert_eq!(ParsePaddedNumberError::TooShort(1, 0), actual_err);
119 }
120
121 #[test]
122 fn non_ascii_digits_error() {
123 let invalid_number = "123abc";
124
125 let actual_err = invalid_number.parse::<PaddedNumber<0, 10>>().unwrap_err();
126
127 assert!(matches!(actual_err, ParsePaddedNumberError::InvalidNumber(_)));
128 }
129
130 #[test]
131 fn is_empty() {
132 let number = PaddedNumber::<0, 0> { leading_zeros: 0, number: 0 };
133 assert!(number.is_empty())
134 }
135
136 #[test]
137 fn length() {
138 assert_len(0, "");
139 assert_len(1, "0");
140 assert_len(3, "000");
141 assert_len(3, "467");
142 assert_len(5, "00467");
143
144 fn assert_len(expected_length: u8, number_str: &str) {
145 let number = mock_from_str::<0, 10>(number_str);
146 assert_eq!(expected_length, number.len());
147 }
148 }
149}