1use super::{Number, NumberLike, NumberType};
2
3use std::{
4 cmp::Ordering,
5 convert::TryFrom,
6 hash::{Hash, Hasher},
7};
8use strum::ParseError;
9
10#[derive(Copy, Clone, Debug)]
12#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
13pub enum Primitive {
14 Bool(bool),
15 Char(char),
16 Number(Number),
17 Unit,
18}
19
20impl Default for PrimitiveType {
21 fn default() -> Self {
23 Self::Unit
24 }
25}
26
27impl Primitive {
28 #[inline]
30 pub fn is_type(&self, r#type: PrimitiveType) -> bool {
31 self.to_type() == r#type
32 }
33
34 #[inline]
36 pub fn to_type(&self) -> PrimitiveType {
37 PrimitiveType::from(self)
38 }
39
40 #[inline]
42 pub fn has_same_type(&self, other: &Primitive) -> bool {
43 self.to_type() == other.to_type()
44 }
45}
46
47impl Hash for Primitive {
48 fn hash<H: Hasher>(&self, state: &mut H) {
49 match self {
50 Self::Bool(x) => x.hash(state),
51 Self::Char(x) => x.hash(state),
52 Self::Number(x) => x.hash(state),
53 Self::Unit => Self::Unit.hash(state),
54 }
55 }
56}
57
58impl Eq for Primitive {}
60
61impl PartialEq for Primitive {
62 fn eq(&self, other: &Self) -> bool {
65 match (self, other) {
66 (Self::Bool(a), Self::Bool(b)) => a == b,
67 (Self::Char(a), Self::Char(b)) => a == b,
68 (Self::Number(a), Self::Number(b)) => a == b,
69 (Self::Unit, Self::Unit) => true,
70 _ => false,
71 }
72 }
73}
74
75impl PartialOrd for Primitive {
76 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
78 match (self, other) {
79 (Self::Bool(a), Self::Bool(b)) => a.partial_cmp(b),
80 (Self::Char(a), Self::Char(b)) => a.partial_cmp(b),
81 (Self::Number(a), Self::Number(b)) => a.partial_cmp(b),
82 (Self::Unit, Self::Unit) => Some(Ordering::Equal),
83 _ => None,
84 }
85 }
86}
87
88pub trait PrimitiveLike: Sized {
90 fn into_primitive(self) -> Primitive;
92
93 fn try_from_primitive(primitive: Primitive) -> Result<Self, Primitive>;
96}
97
98impl PrimitiveLike for Primitive {
99 fn into_primitive(self) -> Primitive {
100 self
101 }
102
103 fn try_from_primitive(primitive: Primitive) -> Result<Self, Primitive> {
104 Ok(primitive)
105 }
106}
107
108impl PrimitiveLike for bool {
109 fn into_primitive(self) -> Primitive {
110 Primitive::Bool(self)
111 }
112
113 fn try_from_primitive(primitive: Primitive) -> Result<Self, Primitive> {
114 match primitive {
115 Primitive::Bool(x) => Ok(x),
116 x => Err(x),
117 }
118 }
119}
120
121impl PrimitiveLike for char {
122 fn into_primitive(self) -> Primitive {
123 Primitive::Char(self)
124 }
125
126 fn try_from_primitive(primitive: Primitive) -> Result<Self, Primitive> {
127 match primitive {
128 Primitive::Char(x) => Ok(x),
129 x => Err(x),
130 }
131 }
132}
133
134impl<T: NumberLike> PrimitiveLike for T {
135 fn into_primitive(self) -> Primitive {
136 Primitive::Number(self.into_number())
137 }
138
139 fn try_from_primitive(primitive: Primitive) -> Result<Self, Primitive> {
140 match primitive {
141 Primitive::Number(x) => T::try_from_number(x).map_err(Primitive::Number),
142 x => Err(x),
143 }
144 }
145}
146
147impl PrimitiveLike for () {
148 fn into_primitive(self) -> Primitive {
149 Primitive::Unit
150 }
151
152 fn try_from_primitive(primitive: Primitive) -> Result<Self, Primitive> {
153 match primitive {
154 Primitive::Unit => Ok(()),
155 x => Err(x),
156 }
157 }
158}
159
160impl From<()> for Primitive {
161 fn from(_: ()) -> Self {
162 Self::Unit
163 }
164}
165
166impl TryFrom<Primitive> for () {
167 type Error = Primitive;
168
169 fn try_from(x: Primitive) -> Result<Self, Self::Error> {
170 PrimitiveLike::try_from_primitive(x)
171 }
172}
173
174macro_rules! impl_conv {
175 ($($type:ty)+) => {$(
176 impl From<$type> for Primitive {
177 fn from(x: $type) -> Self {
178 <$type as PrimitiveLike>::into_primitive(x)
179 }
180 }
181
182 impl TryFrom<Primitive> for $type {
183 type Error = Primitive;
184
185 fn try_from(x: Primitive) -> Result<Self, Self::Error> {
186 <$type as PrimitiveLike>::try_from_primitive(x)
187 }
188 }
189 )+};
190}
191
192impl_conv!(bool char f32 f64 i128 i16 i32 i64 i8 isize u128 u16 u32 u64 u8 usize);
193
194#[derive(Copy, Clone, Debug, PartialEq, Eq)]
196#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
197pub enum PrimitiveType {
198 Bool,
199 Char,
200 Number(NumberType),
201 Unit,
202}
203
204impl PrimitiveType {
205 pub fn is_bool(&self) -> bool {
206 matches!(self, Self::Bool)
207 }
208
209 pub fn is_char(&self) -> bool {
210 matches!(self, Self::Char)
211 }
212
213 pub fn is_number(&self) -> bool {
214 matches!(self, Self::Number(_))
215 }
216
217 pub fn is_unit(&self) -> bool {
218 matches!(self, Self::Unit)
219 }
220
221 pub fn to_number_type(&self) -> Option<NumberType> {
222 match self {
223 Self::Number(x) => Some(*x),
224 _ => None,
225 }
226 }
227
228 pub fn from_type_name(tname: &str) -> Result<Self, ParseError> {
257 use std::str::FromStr;
258
259 match tname {
262 "()" => Self::from_str("unit"),
263 x => Self::from_str(x),
264 }
265 }
266}
267
268impl std::fmt::Display for PrimitiveType {
269 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
270 match self {
271 Self::Bool => write!(f, "bool"),
272 Self::Char => write!(f, "char"),
273 Self::Number(t) => write!(f, "number:{}", t),
274 Self::Unit => write!(f, "unit"),
275 }
276 }
277}
278
279impl From<Primitive> for PrimitiveType {
280 fn from(v: Primitive) -> Self {
281 Self::from(&v)
282 }
283}
284
285impl<'a> From<&'a Primitive> for PrimitiveType {
286 fn from(v: &'a Primitive) -> Self {
287 match v {
288 Primitive::Bool(_) => Self::Bool,
289 Primitive::Char(_) => Self::Char,
290 Primitive::Number(x) => Self::Number(x.to_type()),
291 Primitive::Unit => Self::Unit,
292 }
293 }
294}
295
296impl std::str::FromStr for PrimitiveType {
297 type Err = ParseError;
298
299 fn from_str(s: &str) -> Result<Self, Self::Err> {
316 let mut s_it = s.split(':');
317 let primary = s_it.next();
318 let secondary = s_it.next();
319 let has_more = s_it.next().is_some();
320
321 if has_more {
323 return Err(ParseError::VariantNotFound);
324 }
325
326 match (primary, secondary) {
327 (Some("bool"), None) => Ok(Self::Bool),
328 (Some("char"), None) => Ok(Self::Char),
329 (Some("number"), Some(x)) => Ok(Self::Number(NumberType::from_str(x)?)),
330 (Some("unit"), None) => Ok(Self::Unit),
331 (Some(x), None) => Ok(Self::Number(NumberType::from_str(x)?)),
332 _ => Err(ParseError::VariantNotFound),
333 }
334 }
335}
336
337#[cfg(test)]
338mod tests {
339 use super::*;
340
341 #[test]
342 fn primitive_like_can_convert_number_like_to_primitive() {
343 assert!(matches!(
344 1u8.into_primitive(),
345 Primitive::Number(Number::U8(1)),
346 ));
347 }
348
349 #[test]
350 fn primitive_like_can_convert_primitive_to_number_like() {
351 assert!(matches!(
352 Number::try_from_primitive(Primitive::Number(Number::U8(1))),
353 Ok(Number::U8(1)),
354 ));
355
356 assert!(matches!(
357 Number::try_from_primitive(Primitive::Char('c')),
358 Err(Primitive::Char('c')),
359 ));
360 }
361
362 #[test]
363 fn primitive_like_can_convert_bool_to_primitive() {
364 assert!(matches!(true.into_primitive(), Primitive::Bool(true),));
365 }
366
367 #[test]
368 fn primitive_like_can_convert_primitive_to_bool() {
369 assert!(matches!(
370 bool::try_from_primitive(Primitive::Bool(true)),
371 Ok(true)
372 ));
373
374 assert!(matches!(
375 bool::try_from_primitive(Primitive::Char('c')),
376 Err(Primitive::Char('c')),
377 ));
378 }
379
380 #[test]
381 fn primitive_like_can_convert_char_to_primitive() {
382 assert!(matches!('c'.into_primitive(), Primitive::Char('c')));
383 }
384
385 #[test]
386 fn primitive_like_can_convert_primitive_to_char() {
387 assert!(matches!(
388 char::try_from_primitive(Primitive::Char('c')),
389 Ok('c')
390 ));
391
392 assert!(matches!(
393 char::try_from_primitive(Primitive::Bool(true)),
394 Err(Primitive::Bool(true)),
395 ));
396 }
397
398 #[test]
399 fn primitive_like_can_convert_unit_to_primitive() {
400 assert!(matches!(().into_primitive(), Primitive::Unit));
401 }
402
403 #[test]
404 fn primitive_like_can_convert_primitive_to_unit() {
405 assert!(matches!(<()>::try_from_primitive(Primitive::Unit), Ok(())));
406
407 assert!(matches!(
408 <()>::try_from_primitive(Primitive::Bool(true)),
409 Err(Primitive::Bool(true)),
410 ));
411 }
412}