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