horfimbor_eventsource/
model_key.rs1use crate::StreamName;
4use serde::{Deserialize, Serialize};
5use std::fmt::{Display, Formatter};
6use uuid::{Error as UuidError, Uuid};
7
8use sha1::{Digest, Sha1};
9use thiserror::Error;
10
11#[derive(Deserialize, Serialize, Debug, Clone, Eq, PartialEq, Default, Hash)]
13pub struct ModelKey {
14 stream_name: String,
15 stream_id: Uuid,
16}
17
18impl ModelKey {
20 #[must_use]
23 pub fn new(stream_name: StreamName, stream_id: Uuid) -> Self {
24 let name = stream_name.replace('-', "_");
26 Self {
27 stream_name: name,
28 stream_id,
29 }
30 }
31
32 #[must_use]
34 pub fn new_uuid_v4(stream_name: StreamName) -> Self {
35 let name = stream_name.replace('-', "_");
37 let stream_id = Uuid::new_v4();
38 Self {
39 stream_name: name,
40 stream_id,
41 }
42 }
43
44 #[must_use]
46 pub fn new_uuid_v7(stream_name: StreamName) -> Self {
47 let name = stream_name.replace('-', "_");
49 let stream_id = Uuid::now_v7();
50 Self {
51 stream_name: name,
52 stream_id,
53 }
54 }
55
56 #[must_use]
58 pub fn new_uuid_v8(stream_name: StreamName, kind: &'static str, data: &str) -> Self {
59 let mut hasher = Sha1::new();
60 hasher.update(kind);
61 hasher.update(data);
62 let hash = hasher.finalize();
63 let result = hash.as_slice();
64
65 let mut bytes = [0; 16];
66 bytes.copy_from_slice(&result[..16]);
67
68 let stream_id = Uuid::new_v8(bytes);
69 Self::new(stream_name, stream_id)
70 }
71
72 #[must_use]
74 pub fn format(&self) -> String {
75 format!("{}-{}", self.stream_name.replace('.', "_"), self.stream_id)
76 }
77}
78
79#[derive(Error, Debug)]
81pub enum ModelKeyError {
82 #[error("uuid error`{0}`")]
84 UuidError(#[from] UuidError),
85
86 #[error("the parameter was empty")]
88 Empty,
89}
90
91impl TryFrom<&str> for ModelKey {
92 type Error = ModelKeyError;
93
94 fn try_from(value: &str) -> Result<Self, Self::Error> {
95 let mut split = value.split('-');
96 let Some(stream_name) = split.next() else {
97 return Err(ModelKeyError::Empty);
98 };
99 let to_check_uuid = split.collect::<Vec<&str>>().join("-");
100 let stream_id = Uuid::parse_str(&to_check_uuid)?;
101 Ok(Self {
102 stream_name: stream_name.to_string(),
103 stream_id,
104 })
105 }
106}
107
108impl Display for ModelKey {
109 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
110 write!(f, "{}-{}", self.stream_name, self.stream_id)
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
118
119 #[test]
120 fn test_format() {
121 let m = ModelKey::new(
122 "mpzNpYJ",
123 Uuid::parse_str("01797a2e-19de-467c-bda2-eddc2a2cbf8c").unwrap(),
124 );
125
126 assert_eq!(
127 m.format(),
128 "mpzNpYJ-01797a2e-19de-467c-bda2-eddc2a2cbf8c".to_string()
129 );
130 }
131
132 #[test]
133 fn test_from() {
134 let m = ModelKey::new(
135 "mpzNpYJ",
136 Uuid::parse_str("01797a2e-19de-467c-bda2-eddc2a2cbf8c").unwrap(),
137 );
138
139 let f: ModelKey = "mpzNpYJ-01797a2e-19de-467c-bda2-eddc2a2cbf8c"
140 .try_into()
141 .unwrap();
142
143 assert_eq!(f, m);
144 }
145}