Skip to main content

tetratto_core2/model/
id.rs

1use std::fmt::Display;
2
3use base62::decode;
4use serde::{Deserialize, Deserializer, Serialize, de::Visitor};
5use tritools::id::Id as NewId;
6use uuid::Uuid;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum Id {
10    Next(NewId),
11    Legacy(usize),
12}
13
14impl Id {
15    /// Create a new ID.
16    pub fn new() -> Self {
17        Self::Next(NewId::new())
18    }
19
20    /// Get the ID as a usize.
21    pub fn as_usize(&self) -> usize {
22        match self {
23            Self::Next(x) => x.0.as_u128() as usize,
24            Self::Legacy(x) => *x,
25        }
26    }
27
28    /// Get the printable form of the ID. (base62 UUID)
29    pub fn printable(&self) -> String {
30        match self {
31            Self::Next(x) => x.printable(),
32            Self::Legacy(x) => x.to_string(),
33        }
34    }
35
36    /// Serialize using `serde_json`. Surrounding quotes removed.
37    pub fn serialize(&self) -> String {
38        serde_json::to_string(&self)
39            .unwrap()
40            .trim_matches('"')
41            .to_string()
42    }
43
44    /// Deserialize from a printable format.
45    pub fn deserialize(input: &str) -> Self {
46        if input.is_empty() || input.len() < 12 {
47            return Id::default();
48        }
49
50        if is_numeric(input) {
51            Id::Legacy(input.parse().unwrap())
52        } else {
53            let uuid = Uuid::from_u128(match decode(input) {
54                Ok(x) => x,
55                Err(_) => return Id::default(),
56            });
57
58            Id::Next(tritools::id::Id(uuid))
59        }
60    }
61}
62
63impl Serialize for Id {
64    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
65    where
66        S: serde::Serializer,
67    {
68        match self {
69            Self::Next(x) => serializer.serialize_str(&x.printable()),
70            Self::Legacy(x) => serializer.serialize_str(&x.to_string()),
71        }
72    }
73}
74
75struct IdVisitor;
76
77fn is_numeric(haystack: &str) -> bool {
78    for c in haystack.chars() {
79        if !c.is_numeric() {
80            return false;
81        }
82    }
83
84    true
85}
86
87macro_rules! impl_number_visitor {
88    ($n:ident, $t:ty) => {
89        fn $n<E>(self, v: $t) -> Result<Self::Value, E>
90        where
91            E: serde::de::Error,
92        {
93            Ok(Id::Legacy(v as usize))
94        }
95    };
96}
97
98impl<'de> Visitor<'de> for IdVisitor {
99    type Value = Id;
100
101    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
102        formatter.write_str("a string")
103    }
104
105    fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
106    where
107        E: serde::de::Error,
108    {
109        Ok(Id::deserialize(&v))
110    }
111
112    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
113    where
114        E: serde::de::Error,
115    {
116        Ok(Id::deserialize(v))
117    }
118
119    impl_number_visitor!(visit_f32, f32);
120    impl_number_visitor!(visit_i32, i32);
121    impl_number_visitor!(visit_u32, u32);
122    impl_number_visitor!(visit_i64, i64);
123    impl_number_visitor!(visit_u64, u64);
124    impl_number_visitor!(visit_i128, i128);
125    impl_number_visitor!(visit_u128, u128);
126}
127
128impl<'de> Deserialize<'de> for Id {
129    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
130    where
131        D: Deserializer<'de>,
132    {
133        deserializer.deserialize_any(IdVisitor)
134    }
135}
136
137impl Display for Id {
138    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139        f.write_str(&self.printable())
140    }
141}
142
143impl Default for Id {
144    fn default() -> Self {
145        Self::Legacy(0)
146    }
147}