1use crate::resources::{IDUnknown, TSIDResource};
2use anyhow::anyhow;
3use std::{
4 fmt::{Debug, Display},
5 marker::PhantomData,
6 ops::Deref,
7 str::FromStr,
8};
9use tsid::{TSID, create_tsid};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
20#[cfg_attr(feature = "ts-rs", ts(type = "string", concrete(Resource = IDUnknown)))]
21#[cfg_attr(
22 feature = "diesel",
23 derive(diesel::expression::AsExpression, diesel::deserialize::FromSqlRow)
24)]
25#[cfg_attr(feature = "diesel", diesel(sql_type = diesel::sql_types::BigInt))]
26pub struct TSIDDatabaseID<Resource: TSIDResource> {
27 pub(crate) id: TSID,
28 resource: PhantomData<Resource>,
29}
30
31impl<Resource: TSIDResource> TSIDDatabaseID<Resource> {
32 pub fn from_raw_number(number: u64) -> Self {
35 Self {
36 id: TSID::from(number),
37 resource: PhantomData,
38 }
39 }
40
41 pub fn from_integer(number: i64) -> Self {
42 Self {
43 id: TSID::from(number as u64),
44 resource: PhantomData,
45 }
46 }
47
48 pub fn random() -> Self {
50 Self {
51 id: create_tsid(),
52 resource: PhantomData,
53 }
54 }
55
56 pub fn to_raw_number(&self) -> u64 {
59 self.id.number()
60 }
61
62 pub fn into_unknown(&self) -> TSIDDatabaseID<IDUnknown> {
63 TSIDDatabaseID::<IDUnknown> {
64 id: self.id,
65 resource: PhantomData,
66 }
67 }
68}
69
70impl<Resource: TSIDResource> Display for TSIDDatabaseID<Resource> {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 if let Some(prefix) = Resource::prefix() {
73 write!(f, "{}_{}", prefix, self.id.to_string())
74 } else {
75 write!(f, "{}", self.id.to_string())
76 }
77 }
78}
79
80impl<Resource: TSIDResource> Deref for TSIDDatabaseID<Resource> {
81 type Target = TSID;
82 fn deref(&self) -> &Self::Target {
83 &self.id
84 }
85}
86
87impl<Resource: TSIDResource> From<TSID> for TSIDDatabaseID<Resource> {
88 fn from(value: TSID) -> Self {
89 Self {
90 id: value,
91 resource: PhantomData,
92 }
93 }
94}
95
96impl<Resource: TSIDResource> FromStr for TSIDDatabaseID<Resource> {
97 type Err = anyhow::Error;
98
99 fn from_str(v: &str) -> Result<TSIDDatabaseID<Resource>, anyhow::Error> {
106 let tsid_only = if let Some(prefix) = Resource::prefix() {
107 v.strip_prefix(&format!("{}_", prefix))
108 .ok_or(anyhow!("missing prefix {}_", prefix))?
109 } else {
110 v
111 };
112
113 let tsid = TSID::try_from(tsid_only).map_err(|_| anyhow!("invalid tsid"))?;
114 Ok(TSIDDatabaseID::<Resource>::from(tsid))
115 }
116}