ankurah_proto/
entity_id.rs1use base64::{engine::general_purpose, Engine as _};
2use serde::{Deserialize, Serialize};
3use std::fmt;
4use ulid::Ulid;
5
6use wasm_bindgen::prelude::*;
7
8use crate::DecodeError;
9#[derive(PartialEq, Eq, Hash, Clone, Copy, Ord, PartialOrd, Serialize, Deserialize)]
11#[wasm_bindgen]
12pub struct ID(Ulid);
13impl ID {
14 pub fn new() -> Self { ID(Ulid::new()) }
15
16 pub fn from_ulid(ulid: Ulid) -> Self { ID(ulid) }
17
18 pub fn to_bytes(&self) -> [u8; 16] { self.0.to_bytes() }
19
20 pub fn from_base64(base64_string: &str) -> Result<Self, DecodeError> {
21 let decoded = general_purpose::URL_SAFE_NO_PAD.decode(base64_string).map_err(|e| DecodeError::InvalidBase64(e))?;
22 let bytes: [u8; 16] = decoded[..].try_into().map_err(|_| DecodeError::InvalidLength)?;
23
24 Ok(ID(Ulid::from_bytes(bytes)))
25 }
26
27 pub fn to_base64(&self) -> String { general_purpose::URL_SAFE_NO_PAD.encode(self.0.to_bytes()) }
28}
29
30#[wasm_bindgen]
31impl ID {
32 pub fn as_string(&self) -> String { self.to_base64() }
33}
34
35impl fmt::Display for ID {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { write!(f, "{}", self.to_base64()) }
37}
38impl Into<String> for ID {
39 fn into(self) -> String { self.to_base64() }
40}
41impl TryFrom<&str> for ID {
42 type Error = DecodeError;
43 fn try_from(id: &str) -> Result<Self, Self::Error> {
44 match Self::from_base64(id) {
45 Ok(id) => Ok(id),
46 Err(DecodeError::InvalidLength) => {
47 let ulid = Ulid::from_string(id).map_err(|_| DecodeError::InvalidUlid).map_err(|_| DecodeError::InvalidFallback)?;
50 Ok(ID::from_ulid(ulid))
51 }
52 Err(e) => Err(e),
53 }
54 }
55}
56
57impl TryFrom<String> for ID {
58 type Error = DecodeError;
59 fn try_from(id: String) -> Result<Self, Self::Error> { Self::try_from(id.as_str()) }
60}
61
62impl TryFrom<&String> for ID {
63 type Error = DecodeError;
64 fn try_from(id: &String) -> Result<Self, Self::Error> { Self::try_from(id.as_str()) }
65}
66
67impl TryFrom<JsValue> for ID {
68 type Error = DecodeError;
69 fn try_from(value: JsValue) -> Result<Self, Self::Error> {
70 let id: String = value.as_string().ok_or(DecodeError::NotStringValue)?;
71 id.try_into()
72 }
73}
74
75impl std::fmt::Debug for ID {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0.to_string()) }
77}
78
79impl From<ID> for Ulid {
80 fn from(id: ID) -> Self { id.0 }
81}
82
83impl Default for ID {
84 fn default() -> Self { Self::new() }
85}