1use crate::table::column::{ColumnData, ColumnKind, ColumnType, FromDataError};
2
3use std::borrow::Cow;
4use std::fmt;
5use std::str::FromStr;
6use std::time::{SystemTime, UNIX_EPOCH};
7
8use base64::engine::{general_purpose::URL_SAFE_NO_PAD, Engine};
9use base64::DecodeError;
10use rand::{rngs::OsRng, RngCore};
11
12use serde::de::{Deserializer, Error};
13use serde::ser::Serializer;
14use serde::{Deserialize, Serialize};
15
16#[derive(Clone, Copy, PartialEq, Eq, Hash)]
23#[cfg_attr(feature = "graphql", derive(juniper::GraphQLScalar))]
25#[cfg_attr(feature = "graphql", graphql(with = graphql))]
26pub struct UniqueId([u8; 10]);
27
28impl UniqueId {
29 pub fn new() -> Self {
30 let secs_bytes = SystemTime::now()
31 .duration_since(UNIX_EPOCH)
32 .expect("SystemTime before UNIX EPOCH!")
33 .as_secs()
34 .to_be_bytes();
35
36 let mut bytes = [0u8; 10];
37 bytes[..5].copy_from_slice(&secs_bytes[3..8]);
38
39 OsRng.fill_bytes(&mut bytes[5..]);
40
41 Self(bytes)
42 }
43
44 pub const fn from_raw(inner: [u8; 10]) -> Self {
47 Self(inner)
48 }
49
50 pub fn from_slice_unchecked(slice: &[u8]) -> Self {
51 let mut bytes = [0u8; 10];
52 bytes.copy_from_slice(slice);
53 Self(bytes)
54 }
55
56 pub fn to_b64(&self) -> String {
57 URL_SAFE_NO_PAD.encode(self.0)
58 }
59
60 pub fn parse_from_b64<T>(b64: T) -> Result<Self, DecodeError>
62 where
63 T: AsRef<[u8]>,
64 {
65 if b64.as_ref().len() != 14 {
66 return Err(DecodeError::InvalidLength(b64.as_ref().len()));
67 }
68
69 let mut bytes = [0u8; 10];
70 URL_SAFE_NO_PAD
71 .decode_slice_unchecked(b64, &mut bytes)
72 .map(|n| assert_eq!(n, bytes.len()))
73 .map(|_| Self(bytes))
74 }
75
76 pub fn from_bytes(bytes: [u8; 10]) -> Self {
77 Self(bytes)
78 }
79
80 pub fn into_bytes(self) -> [u8; 10] {
81 self.0
82 }
83
84 pub fn since_unix_secs(&self) -> u64 {
85 let mut bytes = [0u8; 8];
86 bytes[3..].copy_from_slice(&self.0[..5]);
87 u64::from_be_bytes(bytes)
88 }
89
90 pub fn as_slice(&self) -> &[u8] {
91 &self.0
92 }
93}
94
95impl fmt::Debug for UniqueId {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 f.debug_tuple("UniqueId").field(&self.to_b64()).finish()
98 }
99}
100
101impl fmt::Display for UniqueId {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 self.to_b64().fmt(f)
104 }
105}
106
107impl FromStr for UniqueId {
108 type Err = DecodeError;
109
110 fn from_str(s: &str) -> Result<Self, Self::Err> {
111 Self::parse_from_b64(s)
112 }
113}
114
115impl From<DecodeError> for FromDataError {
116 fn from(e: DecodeError) -> Self {
117 Self::CustomString(format!("uniqueid decode error {:?}", e))
118 }
119}
120
121impl ColumnType for UniqueId {
122 fn column_kind() -> ColumnKind {
123 ColumnKind::FixedText(14)
124 }
125
126 fn to_data(&self) -> ColumnData<'_> {
127 ColumnData::Text(self.to_b64().into())
128 }
129
130 fn from_data(data: ColumnData) -> Result<Self, FromDataError> {
131 match data {
132 ColumnData::Text(s) if s.len() == 14 => {
133 Ok(Self::parse_from_b64(s.as_str())?)
134 }
135 _ => Err(FromDataError::ExpectedType(
136 "char with 14 chars for unique id",
137 )),
138 }
139 }
140}
141impl Serialize for UniqueId {
144 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
145 where
146 S: Serializer,
147 {
148 serializer.serialize_str(&self.to_b64())
149 }
150}
151
152impl<'de> Deserialize<'de> for UniqueId {
153 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
154 where
155 D: Deserializer<'de>,
156 {
157 let s: Cow<'_, str> = Deserialize::deserialize(deserializer)?;
158 let s = s.as_ref();
159 if s.len() == 14 {
160 UniqueId::parse_from_b64(s).map_err(D::Error::custom)
161 } else {
162 Err(D::Error::custom(
163 "expected string with exactly 14 characters",
164 ))
165 }
166 }
167}
168
169#[cfg(feature = "protobuf")]
170mod protobuf {
171 use super::*;
172
173 use fire_protobuf::{
174 bytes::BytesWrite,
175 decode::{DecodeError, DecodeMessage, FieldKind},
176 encode::{
177 EncodeError, EncodeMessage, FieldOpt, MessageEncoder, SizeBuilder,
178 },
179 WireType,
180 };
181
182 impl EncodeMessage for UniqueId {
183 const WIRE_TYPE: WireType = WireType::Len;
184
185 fn is_default(&self) -> bool {
186 false
187 }
188
189 fn encoded_size(
190 &mut self,
191 field: Option<FieldOpt>,
192 builder: &mut SizeBuilder,
193 ) -> Result<(), EncodeError> {
194 self.0.encoded_size(field, builder)
195 }
196
197 fn encode<B>(
198 &mut self,
199 field: Option<FieldOpt>,
200 encoder: &mut MessageEncoder<B>,
201 ) -> Result<(), EncodeError>
202 where
203 B: BytesWrite,
204 {
205 self.0.encode(field, encoder)
206 }
207 }
208
209 impl<'m> DecodeMessage<'m> for UniqueId {
210 const WIRE_TYPE: WireType = WireType::Len;
211
212 fn decode_default() -> Self {
213 Self::from_raw([0; 10])
214 }
215
216 fn merge(
217 &mut self,
218 kind: FieldKind<'m>,
219 is_field: bool,
220 ) -> Result<(), DecodeError> {
221 self.0.merge(kind, is_field)
222 }
223 }
224}
225
226#[cfg(feature = "graphql")]
227mod graphql {
228 use super::*;
229
230 use juniper::{
231 Value, ScalarValue, InputValue, ScalarToken, ParseScalarResult
232 };
233
234 pub(crate) fn to_output<S: ScalarValue>(v: &UniqueId) -> Value<S> {
235 Value::scalar(v.to_string())
236 }
237
238 pub(crate) fn from_input<S: ScalarValue>(
239 v: &InputValue<S>
240 ) -> Result<UniqueId, String> {
241 v.as_string_value()
242 .ok_or("Expected a string")?
243 .parse()
244 .map_err(|e: DecodeError| e.to_string())
245 }
246
247 pub(crate) fn parse_token<S: ScalarValue>(
248 value: ScalarToken<'_>
249 ) -> ParseScalarResult<S> {
250 <String as juniper::ParseScalarValue<S>>::from_str(value)
251 }
252}
253
254#[cfg(test)]
255mod tests {
256
257 use super::*;
258 use serde_json::{from_str, from_value, Value};
259
260 #[test]
263 fn serde_test() {
264 let s = "\"AGCGeWIDTlipbg\"";
265 let d: UniqueId = from_str(s).unwrap();
266 assert_eq!(d.to_string(), "AGCGeWIDTlipbg");
267
268 let v = Value::String("AGCGeWIDTlipbg".into());
269 let d: UniqueId = from_value(v).unwrap();
270 assert_eq!(d.to_string(), "AGCGeWIDTlipbg");
271 }
272}