1use crate::error::Error;
2use crate::{with_ct, CellValue};
3use num_traits::{One, Zero};
4#[cfg(feature = "serde")]
5use serde::{Deserialize, Serialize};
6use std::fmt::{Debug, Display, Formatter};
7use std::mem;
8use std::str::FromStr;
9
10macro_rules! cv_enum {
12 ( $(($id:ident, $_p:ident)),*) => {
13 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
15 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16 #[repr(u8)]
17 pub enum CellType { $($id),* }
18 }
19}
20with_ct!(cv_enum);
21
22impl Display for CellType {
24 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
25 Debug::fmt(self, f)
26 }
27}
28
29impl FromStr for CellType {
30 type Err = Error;
31
32 fn from_str(s: &str) -> Result<Self, Self::Err> {
33 macro_rules! str_match {
34 ( $( ($ct:ident, $_p:ident) ),* ) => {
35 match s {
36 $( stringify!($ct) => Ok(CellType::$ct), )*
37 o => Err(Error::ParseError(o.into(), "CellType")),
38 }
39 };
40 }
41 with_ct!(str_match)
42 }
43}
44
45impl CellType {
46 pub fn iter() -> impl Iterator<Item = CellType> {
48 macro_rules! array {
49 ( $( ($id:ident, $_p:ident) ),+) => { [ $( CellType::$id, )+ ] };
50 }
51 with_ct!(array).into_iter()
52 }
53
54 pub fn is_integral(&self) -> bool {
56 match self {
57 CellType::UInt8 => true,
58 CellType::UInt16 => true,
59 CellType::UInt32 => true,
60 CellType::UInt64 => true,
61 CellType::Int8 => true,
62 CellType::Int16 => true,
63 CellType::Int32 => true,
64 CellType::Int64 => true,
65 CellType::Float32 => false,
66 CellType::Float64 => false,
67 }
68 }
69
70 pub fn is_signed(&self) -> bool {
72 match self {
73 CellType::UInt8 => false,
74 CellType::UInt16 => false,
75 CellType::UInt32 => false,
76 CellType::UInt64 => false,
77 CellType::Int8 => true,
78 CellType::Int16 => true,
79 CellType::Int32 => true,
80 CellType::Int64 => true,
81 CellType::Float32 => true,
82 CellType::Float64 => true,
83 }
84 }
85
86 pub fn size_of(&self) -> usize {
88 macro_rules! size_of {
89 ($( ($id:ident, $p:ident) ),* ) => {
90 match self {
91 $(CellType::$id => mem::size_of::<$p>()),*
92 }
93 };
94 }
95 with_ct!(size_of)
96 }
97
98 pub fn union(self, other: Self) -> Self {
100 let min_bytes = {
101 match (self.is_integral(), other.is_integral()) {
102 (true, false) => other.size_of().max(2 * self.size_of()),
103 (false, true) => self.size_of().max(2 * other.size_of()),
104 _ => match (self.is_signed(), other.is_signed()) {
105 (true, false) => self.size_of().max(2 * other.size_of()),
106 (false, true) => other.size_of().max(2 * self.size_of()),
107 _ => self.size_of().max(other.size_of()),
108 },
109 }
110 };
111 let signed = self.is_signed() || other.is_signed();
112 let integral = self.is_integral() && other.is_integral();
113 match (min_bytes, signed, integral) {
115 (1, false, true) => Self::UInt8,
116 (1, true, true) => Self::Int8,
117 (2, false, true) => Self::UInt16,
118 (2, true, true) => Self::Int16,
119 (4, false, true) => Self::UInt32,
120 (4, true, true) => Self::Int32,
121 (4, _, false) => Self::Float32,
122 (8, false, true) => Self::UInt64,
123 (8, true, true) => Self::Int64,
124 _ => Self::Float64,
125 }
126 }
127
128 pub fn can_fit_into(self, other: Self) -> bool {
130 self.union(other) == other
131 }
132
133 pub fn zero(&self) -> CellValue {
135 macro_rules! zero {
136 ( $( ($id:ident, $p:ident) ),+) => {
137 match self {
138 $(Self::$id => <$p>::zero().into(),)*
139 }
140 }
141 }
142 with_ct!(zero)
143 }
144
145 pub fn one(&self) -> CellValue {
147 macro_rules! one {
148 ( $( ($id:ident, $p:ident) ),+) => {
149 match self {
150 $(Self::$id => <$p>::one().into(),)*
151 }
152 }
153 }
154 with_ct!(one)
155 }
156
157 pub fn min_value(&self) -> CellValue {
159 macro_rules! mins {
160 ( $( ($id:ident, $p:ident) ),* ) => {
161 match self {
162 $( CellType::$id => $p::MIN.into(), )*
163 }
164 };
165 }
166 with_ct!(mins)
167 }
168
169 pub fn max_value(&self) -> CellValue {
171 macro_rules! maxs {
172 ( $( ($id:ident, $p:ident) ),* ) => {
173 match self {
174 $( CellType::$id => $p::MAX.into(), )*
175 }
176 };
177 }
178 with_ct!(maxs)
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use crate::{with_ct, CellType};
185 use std::str::FromStr;
186
187 #[test]
188 fn can_union() {
189 assert_eq!(CellType::UInt8.union(CellType::UInt8), CellType::UInt8);
191 assert_eq!(CellType::UInt16.union(CellType::UInt16), CellType::UInt16);
192 assert_eq!(
193 CellType::Float32.union(CellType::Float32),
194 CellType::Float32
195 );
196 assert_eq!(
197 CellType::Float64.union(CellType::Float64),
198 CellType::Float64
199 );
200
201 assert_eq!(CellType::Int16.union(CellType::Float32), CellType::Float32);
203 assert_eq!(CellType::Float32.union(CellType::Int16), CellType::Float32);
204 assert_eq!(CellType::UInt8.union(CellType::UInt16), CellType::UInt16);
206 assert_eq!(CellType::Int32.union(CellType::Float32), CellType::Float64);
207 }
208
209 #[test]
210 fn is_integral() {
211 assert!(CellType::UInt8.is_integral());
212 assert!(CellType::UInt16.is_integral());
213 assert!(!CellType::Float32.is_integral());
214 assert!(!CellType::Float64.is_integral());
215 }
216
217 #[test]
218 fn size() {
219 assert_eq!(CellType::Int8.size_of(), 1);
220 assert_eq!(CellType::UInt8.size_of(), 1);
221 assert_eq!(CellType::Int16.size_of(), 2);
222 assert_eq!(CellType::UInt16.size_of(), 2);
223 assert_eq!(CellType::Int32.size_of(), 4);
224 assert_eq!(CellType::UInt32.size_of(), 4);
225 assert_eq!(CellType::Int64.size_of(), 8);
226 assert_eq!(CellType::UInt64.size_of(), 8);
227 assert_eq!(CellType::Float32.size_of(), 4);
228 assert_eq!(CellType::Float64.size_of(), 8);
229 }
230
231 #[test]
232 fn has_min_max() {
233 macro_rules! test {
235 ( $( ($ct:ident, $p:ident) ),* ) => {
236 $(
237 assert_eq!(CellType::$ct.min_value(), $p::MIN.into(), "min");
238 assert_eq!(CellType::$ct.max_value(), $p::MAX.into(), "max");
239 )*
240 };
241 }
242 with_ct!(test);
243 }
244
245 #[test]
246 fn can_string() {
247 macro_rules! test {
249 ( $( ($ct:ident, $p:ident) ),* ) => {
250 $( assert_eq!(CellType::$ct.to_string(), stringify!($ct)); )*
251 };
252 }
253 with_ct!(test);
254
255 for ct in CellType::iter() {
257 let stringed = ct.to_string();
258 let parsed = CellType::from_str(&stringed);
259 assert!(parsed.is_ok(), "{stringed}");
260 assert_eq!(parsed.unwrap(), ct, "{stringed}");
261 }
262
263 assert!(CellType::from_str("UInt57").is_err());
264 }
265
266 #[test]
267 fn zero_one() {
268 macro_rules! test {
269 ( $( ($ct:ident, $p:ident) ),* ) => {
270 $({
271 let zero = CellType::$ct.zero();
272 let one = CellType::$ct.one();
273 assert_eq!(one + zero, one);
274 })*
275 };
276 }
277 with_ct!(test);
278 }
279}