1use std::cmp::Ordering;
2
3use crate::ConstId;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
7pub struct OrderedIdArray<const SIZE: usize> {
8 ids: [ConstId; SIZE],
9}
10
11impl<const SIZE: usize> OrderedIdArray<SIZE> {
12 pub const fn new(ids: [ConstId; SIZE]) -> Self {
14 Self {
15 ids: const_sort(ids),
16 }
17 }
18
19 pub const fn as_raw_slice(&self) -> &[ConstId] {
21 &self.ids
22 }
23
24 pub const fn as_slice(&self) -> OrderedIdSlice {
26 OrderedIdSlice::from_arr(self)
27 }
28
29 pub const fn into_raw(self) -> [ConstId; SIZE] {
31 self.ids
32 }
33
34 pub const fn into_unique(self) -> Result<UniqueIdArray<SIZE>, Self> {
38 match UniqueIdArray::new(self.ids) {
39 Some(unique) => Ok(unique),
40 None => Err(self),
41 }
42 }
43
44 crate::impl_cmp!();
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
49pub struct OrderedIdSlice<'a> {
50 ids: &'a [ConstId],
51}
52
53impl<'a> OrderedIdSlice<'a> {
54 pub const fn from_arr<const SIZE: usize>(arr: &'a OrderedIdArray<SIZE>) -> Self {
56 Self { ids: &arr.ids }
57 }
58
59 pub const fn as_raw_slice(&self) -> &[ConstId] {
61 &self.ids
62 }
63
64 crate::impl_cmp!();
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
69pub struct UniqueIdArray<const SIZE: usize> {
70 ids: [ConstId; SIZE],
71}
72
73impl<const SIZE: usize> UniqueIdArray<SIZE> {
74 pub const fn new(ids: [ConstId; SIZE]) -> Option<Self> {
78 let sorted = const_sort(ids);
79
80 let mut i = 1;
81 while i < SIZE {
82 let left = sorted[i - 1].raw_value();
83 let right = sorted[i].raw_value();
84 if left == right {
85 return None;
86 }
87
88 i += 1;
89 }
90
91 Some(Self { ids: sorted })
92 }
93
94 pub const fn as_raw_slice(&self) -> &[ConstId] {
96 &self.ids
97 }
98
99 pub const fn as_slice(&self) -> UniqueIdSlice {
101 UniqueIdSlice::from_arr(self)
102 }
103
104 pub const fn into_raw(&self) -> [ConstId; SIZE] {
106 self.ids
107 }
108
109 pub const fn into_ordered(&self) -> OrderedIdArray<SIZE> {
111 OrderedIdArray { ids: self.ids }
112 }
113
114 crate::impl_cmp!();
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
119pub struct UniqueIdSlice<'a> {
120 ids: &'a [ConstId],
121}
122
123impl<'a> UniqueIdSlice<'a> {
124 pub const fn from_arr<const SIZE: usize>(arr: &'a UniqueIdArray<SIZE>) -> Self {
126 Self { ids: &arr.ids }
127 }
128
129 pub const fn as_raw_slice(&self) -> &[ConstId] {
131 &self.ids
132 }
133
134 crate::impl_cmp!();
135}
136
137#[macro_export]
138macro_rules! ordered_ids {
139 [$( $item:ty ),* $(,)?] => {
140 $crate::OrderedIdArray::new([$(
141 <$item as $crate::ConstIdentify>::CONST_ID,
142 )*])
143 };
144}
145
146#[macro_export]
147macro_rules! unique_ids {
148 [$( $item:ty ),* $(,)?] => {
149 $crate::UniqueIdArray::new([$(
150 <$item as $crate::ConstIdentify>::CONST_ID,
151 )*])
152 };
153}
154
155const fn const_sort<const SIZE: usize>(mut arr: [ConstId; SIZE]) -> [ConstId; SIZE] {
157 loop {
160 let mut swapped = false;
161 let mut i = 1;
162 while i < SIZE {
163 let left = arr[i - 1].raw_value();
164 let right = arr[i].raw_value();
165 if left > right {
166 arr[i - 1] = ConstId::from_raw(right);
167 arr[i] = ConstId::from_raw(left);
168 swapped = true;
169 }
170
171 i += 1;
172 }
173 if !swapped {
174 break;
175 }
176 }
177
178 arr
179}
180
181const fn const_cmp(slice1: &[ConstId], slice2: &[ConstId]) -> Ordering {
182 let mut i = 0;
184 while i < slice1.len() {
185 if i >= slice2.len() {
188 return Ordering::Greater;
189 }
190
191 let left = slice1[i].raw_value();
193 let right = slice2[i].raw_value();
194 if left > right {
195 return Ordering::Greater;
197 } else if left < right {
198 return Ordering::Less;
200 }
201
202 i += 1;
204 }
205
206 if i < slice2.len() {
209 return Ordering::Less;
210 }
211
212 Ordering::Equal
214}
215
216#[macro_use]
217mod sealed {
218 #[macro_export]
219 macro_rules! impl_cmp {
220 () => {
221 pub const fn const_cmp_ordered<const SIZE2: usize>(
223 &self,
224 other: &OrderedIdArray<SIZE2>,
225 ) -> Ordering {
226 const_cmp(self.as_raw_slice(), other.as_raw_slice())
227 }
228
229 pub const fn const_cmp_ordered_slice(&self, other: &OrderedIdSlice) -> Ordering {
231 const_cmp(self.as_raw_slice(), other.as_raw_slice())
232 }
233
234 pub const fn const_cmp_unique<const SIZE2: usize>(
236 &self,
237 other: &UniqueIdArray<SIZE2>,
238 ) -> Ordering {
239 const_cmp(self.as_raw_slice(), other.as_raw_slice())
240 }
241
242 pub const fn const_cmp_unique_slice(&self, other: &UniqueIdSlice) -> Ordering {
244 const_cmp(self.as_raw_slice(), other.as_raw_slice())
245 }
246 };
247 }
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253
254 const ID1: ConstId = ConstId::from_raw(0);
255 const ID2: ConstId = ConstId::from_raw(1);
256 const ID3: ConstId = ConstId::from_raw(2);
257
258 #[test]
259 fn ordered() {
260 let ordered = OrderedIdArray::new([ID2, ID1, ID3]);
261 let slice = ordered.as_raw_slice();
262 assert!(slice[0] < slice[1]);
263 assert!(slice[1] < slice[2]);
264 }
265
266 #[test]
267 fn unique() {
268 assert!(UniqueIdArray::new([ID2, ID1, ID3, ID2]).is_none());
269 let unique = UniqueIdArray::new([ID3, ID1, ID2]).unwrap();
270 let slice = unique.as_raw_slice();
271 assert!(slice[0] < slice[1]);
272 assert!(slice[1] < slice[2]);
273 }
274
275 #[test]
276 fn compare() {
277 let ordered = OrderedIdArray::new([ID2, ID1]);
278 let unique = UniqueIdArray::new([ID3, ID1]).unwrap();
279 let ordered_eq = OrderedIdArray::new([ID1, ID2]);
280 assert!(ordered.const_cmp_unique(&unique).is_lt());
281 assert!(unique.const_cmp_ordered(&ordered).is_gt());
282 assert!(ordered.const_cmp_ordered(&ordered_eq).is_eq());
283 }
284
285 #[test]
286 fn convert() {
287 let ordered = OrderedIdArray::new([ID3, ID1, ID2]);
288 assert!(ordered.into_unique().is_ok());
289
290 let ordered_duplicate = OrderedIdArray::new([ID1, ID2, ID1]);
291 assert!(ordered_duplicate.into_unique().is_err());
292 }
293}