1#![cfg_attr(not(feature = "std"), no_std)]
20
21pub use codec::{Decode, Encode};
22pub use core::{ops::Add, result};
23pub use enumn::N;
24pub use paste;
25pub use sp_runtime::traits::CheckedSub;
26
27#[macro_export]
49macro_rules! generate_slot_range{
50 ($( $x:ident ( $e:expr ) ),*) => {
52 $crate::generate_lease_period_per_slot!( $( $x )* );
53 $crate::generate_slot_range!(@inner
54 { }
55 $( $x ( $e ) )*
56 );
57 };
58 (@inner
60 { $( $parsed:ident ( $t1:expr, $t2:expr ) )* }
61 $current:ident ( $ce:expr )
62 $( $remaining:ident ( $re:expr ) )*
63 ) => {
64 $crate::paste::paste! {
65 $crate::generate_slot_range!(@inner
66 {
67 $( $parsed ( $t1, $t2 ) )*
68 [< $current $current >] ( $ce, $ce )
69 $( [< $current $remaining >] ($ce, $re) )*
70 }
71 $( $remaining ( $re ) )*
72 );
73 }
74 };
75 (@inner
76 { $( $parsed:ident ( $t1:expr, $t2:expr ) )* }
77 ) => {
78 $crate::generate_slot_range_enum!(@inner $( $parsed )* );
79
80 $crate::generate_slot_range_count!( $( $parsed )* );
81
82 #[cfg(feature = "std")]
83 impl std::fmt::Debug for SlotRange {
84 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
85 let p = self.as_pair();
86 write!(fmt, "[{}..{}]", p.0, p.1)
87 }
88 }
89
90 impl SlotRange {
91 pub const LEASE_PERIODS_PER_SLOT: usize = LEASE_PERIODS_PER_SLOT;
92 pub const SLOT_RANGE_COUNT: usize = SLOT_RANGE_COUNT;
93
94 $crate::generate_slot_range_as_pair!(@inner $( $parsed ( $t1, $t2 ) )* );
95
96 $crate::generate_slot_range_len!(@inner $( $parsed ( $t1, $t2 ) )* );
97
98 $crate::generate_slot_range_new_bounded!(@inner $( $parsed ( $t1, $t2 ) )* );
99 }
100 };
101}
102
103#[macro_export]
104#[doc(hidden)]
105macro_rules! generate_slot_range_enum {
106 (@inner
107 $( $parsed:ident )*
108 ) => {
109 #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, $crate::Encode, $crate::Decode, $crate::N)]
111 #[repr(u8)]
112 pub enum SlotRange { $( $parsed ),* }
113 };
114}
115
116#[macro_export]
117#[doc(hidden)]
118macro_rules! generate_slot_range_as_pair {
119 (@inner
120 $( $parsed:ident ( $t1:expr, $t2:expr ) )*
121 ) => {
122 pub fn intersects(&self, other: SlotRange) -> bool {
124 let a = self.as_pair();
125 let b = other.as_pair();
126 b.0 <= a.1 && a.0 <= b.1
127 }
129
130 pub fn as_pair(&self) -> (u8, u8) {
134 match self {
135 $( SlotRange::$parsed => { ($t1, $t2) } )*
136 }
137 }
138 };
139}
140
141#[macro_export]
142#[doc(hidden)]
143macro_rules! generate_slot_range_len {
144 (@inner
146 $( $parsed:ident ( $t1:expr, $t2:expr ) )*
147 ) => {
148 pub fn len(&self) -> usize {
152 match self {
153 $( SlotRange::$parsed => { ( $t2 - $t1 + 1) } )*
155 }
156 }
157 };
158}
159
160#[macro_export]
161#[doc(hidden)]
162macro_rules! generate_slot_range_new_bounded {
163 (@inner
164 $( $parsed:ident ( $t1:expr, $t2:expr ) )*
165 ) => {
166 pub fn new_bounded<
171 Index: $crate::Add<Output=Index> + $crate::CheckedSub + Copy + Ord + From<u32> + TryInto<u32>
172 >(
173 current: Index,
174 first: Index,
175 last: Index
176 ) -> $crate::result::Result<Self, &'static str> {
177 if first > last || first < current || last >= current + (LEASE_PERIODS_PER_SLOT as u32).into() {
178 return Err("Invalid range for this auction")
179 }
180 let count: u32 = last.checked_sub(&first)
181 .ok_or("range ends before it begins")?
182 .try_into()
183 .map_err(|_| "range too big")?;
184 let first: u32 = first.checked_sub(¤t)
185 .ok_or("range begins too early")?
186 .try_into()
187 .map_err(|_| "start too far")?;
188 match (first, first + count) {
189 $( ($t1, $t2) => { Ok(SlotRange::$parsed) })*
190 _ => Err("bad range"),
191 }
192 }
193 };
194}
195
196#[macro_export]
197#[doc(hidden)]
198macro_rules! generate_slot_range_count {
199 (
200 $start:ident $( $rest:ident )*
201 ) => {
202 $crate::generate_slot_range_count!(@inner 1; $( $rest )*);
203 };
204 (@inner
205 $count:expr;
206 $start:ident $( $rest:ident )*
207 ) => {
208 $crate::generate_slot_range_count!(@inner $count + 1; $( $rest )*);
209 };
210 (@inner
211 $count:expr;
212 ) => {
213 const SLOT_RANGE_COUNT: usize = $count;
214 };
215}
216
217#[macro_export]
218#[doc(hidden)]
219macro_rules! generate_lease_period_per_slot {
220 (
221 $start:ident $( $rest:ident )*
222 ) => {
223 $crate::generate_lease_period_per_slot!(@inner 1; $( $rest )*);
224 };
225 (@inner
226 $count:expr;
227 $start:ident $( $rest:ident )*
228 ) => {
229 $crate::generate_lease_period_per_slot!(@inner $count + 1; $( $rest )*);
230 };
231 (@inner
232 $count:expr;
233 ) => {
234 const LEASE_PERIODS_PER_SLOT: usize = $count;
235 };
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241
242 #[test]
243 fn slot_range_4_works() {
244 generate_slot_range!(Zero(0), One(1), Two(2), Three(3));
245
246 assert_eq!(SlotRange::LEASE_PERIODS_PER_SLOT, 4);
247 assert_eq!(SlotRange::SLOT_RANGE_COUNT, 10);
249 assert_eq!(SlotRange::new_bounded(0u32, 1u32, 2u32).unwrap(), SlotRange::OneTwo);
250 assert_eq!(SlotRange::new_bounded(5u32, 6u32, 7u32).unwrap(), SlotRange::OneTwo);
251 assert!(SlotRange::new_bounded(10u32, 6u32, 7u32).is_err());
252 assert!(SlotRange::new_bounded(10u32, 16u32, 17u32).is_err());
253 assert!(SlotRange::new_bounded(10u32, 11u32, 10u32).is_err());
254 assert_eq!(SlotRange::TwoTwo.len(), 1);
255 assert_eq!(SlotRange::OneTwo.len(), 2);
256 assert_eq!(SlotRange::ZeroThree.len(), 4);
257 assert!(SlotRange::ZeroOne.intersects(SlotRange::OneThree));
258 assert!(!SlotRange::ZeroOne.intersects(SlotRange::TwoThree));
259 assert_eq!(SlotRange::ZeroZero.as_pair(), (0, 0));
260 assert_eq!(SlotRange::OneThree.as_pair(), (1, 3));
261 }
262
263 #[test]
264 fn slot_range_8_works() {
265 generate_slot_range!(Zero(0), One(1), Two(2), Three(3), Four(4), Five(5), Six(6), Seven(7));
266
267 assert_eq!(SlotRange::LEASE_PERIODS_PER_SLOT, 8);
268 assert_eq!(SlotRange::SLOT_RANGE_COUNT, 36);
270 assert_eq!(SlotRange::new_bounded(0u32, 1u32, 2u32).unwrap(), SlotRange::OneTwo);
271 assert_eq!(SlotRange::new_bounded(5u32, 6u32, 7u32).unwrap(), SlotRange::OneTwo);
272 assert!(SlotRange::new_bounded(10u32, 6u32, 7u32).is_err());
273 assert_eq!(SlotRange::new_bounded(10u32, 16u32, 17u32).unwrap(), SlotRange::SixSeven);
275 assert!(SlotRange::new_bounded(10u32, 17u32, 18u32).is_err());
276 assert!(SlotRange::new_bounded(10u32, 20u32, 21u32).is_err());
277 assert!(SlotRange::new_bounded(10u32, 11u32, 10u32).is_err());
278 assert_eq!(SlotRange::TwoTwo.len(), 1);
279 assert_eq!(SlotRange::OneTwo.len(), 2);
280 assert_eq!(SlotRange::ZeroThree.len(), 4);
281 assert_eq!(SlotRange::ZeroSeven.len(), 8);
282 assert!(SlotRange::ZeroOne.intersects(SlotRange::OneThree));
283 assert!(!SlotRange::ZeroOne.intersects(SlotRange::TwoThree));
284 assert!(SlotRange::FiveSix.intersects(SlotRange::SixSeven));
285 assert!(!SlotRange::ThreeFive.intersects(SlotRange::SixSeven));
286 assert_eq!(SlotRange::ZeroZero.as_pair(), (0, 0));
287 assert_eq!(SlotRange::OneThree.as_pair(), (1, 3));
288 assert_eq!(SlotRange::SixSeven.as_pair(), (6, 7));
289 }
290}