pickems/datatypes/
seed.rs1use serde::{Deserialize, Deserializer, Serialize, Serializer};
2
3#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
7pub struct Seed(u16);
8
9impl Seed {
10 pub fn try_new(seed: u16) -> anyhow::Result<Self> {
11 if !(1..=16).contains(&seed) {
12 anyhow::bail!("invalid seed: must be between 1 and 16");
13 }
14
15 Ok(Self(seed))
16 }
17
18 #[must_use]
19 pub fn new(seed: u16) -> Self {
20 Self::try_new(seed).unwrap()
21 }
22
23 pub fn iter_all() -> impl Iterator<Item = Self> {
25 (1..=16).map(|i| Self::try_new(i).unwrap())
26 }
27}
28
29impl std::fmt::Display for Seed {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 self.0.fmt(f)
32 }
33}
34
35impl Serialize for Seed {
36 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
37 serializer.serialize_u16(self.0)
38 }
39}
40
41impl<'de> Deserialize<'de> for Seed {
42 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
43 Self::try_new(u16::deserialize(deserializer)?).map_err(serde::de::Error::custom)
44 }
45}
46
47#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
52#[repr(transparent)]
53pub struct Index(u16);
54
55impl Index {
56 #[inline]
60 #[must_use]
61 pub const fn new<const N: u16>() -> Self {
62 const { assert!(N < 16, "invalid index value (must be less than 16)") };
63 Self(N)
64 }
65
66 #[inline]
68 pub fn try_new(n: u16) -> anyhow::Result<Self> {
69 if n < 16 {
70 Ok(Self(n))
71 } else {
72 Err(anyhow::anyhow!("invalid team index: {n}"))
73 }
74 }
75
76 #[inline]
78 #[must_use]
79 pub const fn to_u16(self) -> u16 {
80 self.0
81 }
82
83 #[inline]
85 #[must_use]
86 pub const fn to_usize(self) -> usize {
87 self.0 as usize
88 }
89
90 #[inline]
92 #[must_use]
93 pub fn to_seed(self) -> Seed {
94 Seed::try_new(self.0 + 1).unwrap()
95 }
96
97 #[inline]
99 #[must_use]
100 pub const fn bit_select(self) -> u16 {
101 1 << self.0
102 }
103
104 #[inline]
107 #[must_use]
108 pub const unsafe fn from_u16(n: u16) -> Self {
109 Self(n)
110 }
111
112 #[inline]
115 #[must_use]
116 pub const unsafe fn from_u32(n: u32) -> Self {
117 Self(n as u16)
118 }
119
120 #[inline]
123 #[must_use]
124 pub const unsafe fn from_usize(n: usize) -> Self {
125 Self(n as u16)
126 }
127
128 pub fn iter_all() -> impl Iterator<Item = Self> {
130 (0..16).map(|i| unsafe { Self::from_u16(i) })
131 }
132}
133
134impl std::fmt::Display for Index {
135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 write!(f, "{}", self.0)
137 }
138}
139
140impl From<Index> for Seed {
141 fn from(index: Index) -> Self {
142 Self(index.0 + 1)
145 }
146}
147
148impl From<Seed> for Index {
149 fn from(seed: Seed) -> Self {
150 Self(seed.0 - 1)
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[derive(Deserialize)]
161 struct Input {
162 seed: Seed,
163 }
164
165 #[test]
166 fn validates_seeds() {
167 assert!(Seed::try_new(1).is_ok());
168 assert!(Seed::try_new(16).is_ok());
169 assert!(Seed::try_new(0).is_err());
170 assert!(Seed::try_new(17).is_err());
171 }
172
173 #[test]
174 fn rejects_invalid_deserialized_seeds() {
175 let input: Input = toml::from_str("seed = 16").unwrap();
176
177 assert_eq!(input.seed.to_string(), "16");
178 assert!(toml::from_str::<Input>("seed = 0").is_err());
179 assert!(toml::from_str::<Input>("seed = 17").is_err());
180 }
181}