padded_number/section.rs
1use crate::*;
2
3impl<const MIN_LENGTH: u8, const MAX_LENGTH: u8> PaddedNumber<MIN_LENGTH, MAX_LENGTH> {
4 /// Get a section of a padded number
5 ///
6 /// First generic parameter is the start index, inclusive. Second parameter
7 /// denotes the end index, exclusive. Remaining bound checks are enforced by
8 /// the type system. E.g. end >= start and end <= max length.
9 ///
10 /// Returns `None` if the end index overflowed for a padded whose length is
11 /// is not equal to it's max length.
12 ///
13 /// # Examples
14 ///
15 /// ```rust
16 /// #![feature(generic_const_exprs)]
17 ///
18 /// # use padded_number_macros::*;
19 /// let section = padded_number!("00123")
20 /// .section::<2, 5>()
21 /// .expect("section should not have overflowed");
22 ///
23 /// assert_eq!(section, bound_padded_number!(3, 3, "123"));
24 ///
25 /// let section = bound_padded_number!(1, 3, "0").section::<1, 3>();
26 /// // overflowed, missing two digits after "0"
27 /// assert!(section.is_none());
28 /// ```
29 ///
30 /// ```compile_fail
31 /// #![feature(generic_const_exprs)]
32 ///
33 /// # use padded_number_macros::*;
34 /// let section = bound_padded_number!(3, 3, "123");
35 /// section.section::<0, 4>(); // <-- END_INDEX '4' > MAX_LENGTH '3'
36 /// ```
37 ///
38 /// ```compile_fail
39 /// #![feature(generic_const_exprs)]
40 ///
41 /// # use padded_number_macros::*;
42 /// let section = bound_padded_number!(3, 3, "123");
43 /// section.section::<2, 1>(); // <-- END_INDEX '1' < START_INDEX '2'
44 /// ```
45 pub fn section<const START_INDEX: u8, const END_INDEX: u8>(
46 &self,
47 ) -> Option<PaddedNumber<{ END_INDEX - START_INDEX }, { END_INDEX - START_INDEX }>>
48 where
49 // Expresses "END_INDEX <= MAX_LENGTH" with current
50 // `generic_const_exprs` unstable feature capabilities
51 [(); { MAX_LENGTH - END_INDEX } as usize]:,
52 {
53 if END_INDEX > self.len() {
54 return None;
55 }
56
57 Some(self.section_impl())
58 }
59
60 /// Get a section from the minimum length of a padded number
61 ///
62 /// Unlike [`PaddedNumber::section`], this does not need to return an
63 /// option. Type system ensures that END_INDEX <= MIN_LENGTH.
64 ///
65 /// # Examples
66 ///
67 /// ```rust
68 /// #![feature(generic_const_exprs)]
69 ///
70 /// # use padded_number_macros::*;
71 /// let section = bound_padded_number!(3, 5, "00123").expected_section::<0, 3>();
72 /// assert_eq!(section, bound_padded_number!(3, 3, "001"));
73 /// ```
74 ///
75 /// ```compile_fail
76 /// #![feature(generic_const_exprs)]
77 ///
78 /// # use padded_number_macros::*;
79 /// let section = bound_padded_number!(3, 5, "00123");
80 /// section.expected_section::<0, 4>(); // <-- END_INDEX '4' > MIN_LENGTH '3'
81 /// ```
82 pub fn expected_section<const START_INDEX: u8, const END_INDEX: u8>(
83 &self,
84 ) -> PaddedNumber<{ END_INDEX - START_INDEX }, { END_INDEX - START_INDEX }>
85 where
86 // Expresses "END_INDEX <= MIN_LENGTH" with current
87 // `generic_const_exprs` unstable feature capabilities
88 [(); { MIN_LENGTH - END_INDEX } as usize]:,
89 // Bound required to call `Self::section_impl`
90 [(); { MAX_LENGTH - END_INDEX } as usize]:,
91 {
92 // save to call since END_INDEX <= MIN_LENGTH
93 self.section_impl()
94 }
95
96 /// Assumes:
97 /// - END_INDEX <= self.len()
98 fn section_impl<const START_INDEX: u8, const END_INDEX: u8>(
99 &self,
100 ) -> PaddedNumber<{ END_INDEX - START_INDEX }, { END_INDEX - START_INDEX }>
101 where
102 // Expresses "END_INDEX <= MAX_LENGTH" with current
103 // `generic_const_exprs` unstable feature capabilities
104 [(); { MAX_LENGTH - END_INDEX } as usize]:,
105 {
106 let leading_zeros = self.leading_zeros;
107
108 let (new_leading_zeros, new_number) = match (
109 START_INDEX.checked_sub(leading_zeros),
110 END_INDEX.checked_sub(leading_zeros),
111 ) {
112 (Some(translated_start), Some(translated_end)) => {
113 (0, self.number_subsection(translated_start, translated_end))
114 }
115 (None, Some(translated_end)) => (leading_zeros - START_INDEX, self.number_subsection(0, translated_end)),
116 (None, None) => {
117 let leading_zero_start = leading_zeros - START_INDEX;
118 let leading_zero_end = leading_zeros - END_INDEX;
119 (leading_zero_start - leading_zero_end, 0)
120 }
121 (Some(_), None) => {
122 // should not be possible with const generic expression
123 // `{ END_INDEX - START_INDEX }` from method signature
124 unreachable!("encountered start > end")
125 }
126 };
127
128 PaddedNumber { leading_zeros: new_leading_zeros, number: new_number }
129 }
130
131 /// Assumes:
132 /// - start <= number_length
133 /// - end <= number_length
134 pub(crate) const fn number_subsection(&self, start: u8, end: u8) -> u64 {
135 let number_length = self.number_len();
136
137 if number_length == 0 {
138 return 0;
139 }
140
141 let left_shifts = start;
142 let right_shifts = number_length - end;
143
144 let mut number = self.number;
145 number = left_shift_repeated(number, number_length, left_shifts);
146 number = right_shift_repeated(number, right_shifts);
147
148 return number;
149
150 // assumes repetitions <= number_length, and number_length > 0
151 const fn left_shift_repeated(number: u64, number_length: u8, repetitions: u8) -> u64 {
152 let mut current = number;
153 let mut current_length = number_length;
154 let mut repetitions_left = repetitions;
155
156 while repetitions_left > 0 {
157 current = left_shift(current, current_length);
158 current_length -= 1;
159 repetitions_left -= 1;
160 }
161
162 current
163 }
164
165 // assumes number_length > 0
166 const fn left_shift(number: u64, number_length: u8) -> u64 {
167 let decimal = 10u64.pow((number_length - 1) as u32);
168 number - (number / decimal) * decimal
169 }
170
171 const fn right_shift_repeated(number: u64, repetitions: u8) -> u64 {
172 number / 10u64.pow(repetitions as u32)
173 }
174 }
175}