kalamdb_commons/models/ids/
table_id.rs1use std::fmt;
5
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7
8use super::namespace_id::NamespaceId;
9use crate::{
10 models::schemas::TableName,
11 storage_key::{decode_key, encode_key, encode_prefix},
12 StorageKey,
13};
14
15#[derive(Debug, Clone, PartialEq, Eq, Hash)]
25pub struct TableId {
26 namespace_id: NamespaceId,
27 table_name: TableName,
28}
29
30impl TableId {
31 #[inline]
33 pub fn new(namespace_id: NamespaceId, table_name: TableName) -> Self {
34 Self {
35 namespace_id,
36 table_name,
37 }
38 }
39
40 #[inline]
42 pub fn namespace_id(&self) -> &NamespaceId {
43 &self.namespace_id
44 }
45
46 #[inline]
48 pub fn table_name(&self) -> &TableName {
49 &self.table_name
50 }
51
52 #[inline]
54 pub fn from_strings(namespace_id: &str, table_name: &str) -> Self {
55 Self {
56 namespace_id: NamespaceId::new(namespace_id),
57 table_name: TableName::new(table_name),
58 }
59 }
60
61 #[inline]
63 pub fn try_from_strings(namespace_id: &str, table_name: &str) -> Result<Self, String> {
64 let namespace_id = NamespaceId::try_parse_reference(namespace_id)
65 .map_err(|e| format!("invalid namespace_id '{}': {}", namespace_id, e))?;
66 let table_name = TableName::try_new(table_name)
67 .map_err(|e| format!("invalid table_name '{}': {}", table_name, e))?;
68
69 Ok(Self {
70 namespace_id,
71 table_name,
72 })
73 }
74
75 #[inline]
77 pub fn namespace_prefix(namespace_id: &NamespaceId) -> Vec<u8> {
78 encode_prefix(&(namespace_id.as_str(),))
79 }
80
81 #[inline]
83 pub fn as_storage_key(&self) -> Vec<u8> {
84 encode_key(&(self.namespace_id.as_str(), self.table_name.as_str()))
85 }
86
87 pub fn from_storage_key(key: &[u8]) -> Option<Self> {
89 if let Ok((namespace_id, table_name)) = decode_key::<(String, String)>(key) {
90 return Some(Self {
91 namespace_id: NamespaceId::new(namespace_id),
92 table_name: TableName::new(table_name),
93 });
94 }
95
96 None
97 }
98
99 pub fn into_parts(self) -> (NamespaceId, TableName) {
101 (self.namespace_id, self.table_name)
102 }
103
104 pub fn full_name(&self) -> String {
109 format!("{}.{}", self.namespace_id.as_str(), self.table_name.as_str())
110 }
111}
112
113impl Serialize for TableId {
115 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
116 where
117 S: Serializer,
118 {
119 serializer.serialize_str(&self.full_name())
120 }
121}
122
123impl<'de> Deserialize<'de> for TableId {
126 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
127 where
128 D: Deserializer<'de>,
129 {
130 use std::fmt;
131
132 use serde::de::{Error, Visitor};
133
134 struct TableIdVisitor;
135
136 impl<'de> Visitor<'de> for TableIdVisitor {
137 type Value = TableId;
138
139 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
140 formatter.write_str("a string in the format 'namespace.table'")
141 }
142
143 fn visit_str<E>(self, value: &str) -> Result<TableId, E>
144 where
145 E: Error,
146 {
147 let mut parts = value.splitn(2, '.');
149 let namespace = parts.next();
150 let table = parts.next();
151 match (namespace, table) {
152 (Some(namespace), Some(table)) => Ok(TableId {
153 namespace_id: NamespaceId::new(namespace),
154 table_name: TableName::new(table),
155 }),
156 _ => Err(E::custom(format!("Invalid table_id format: {}", value))),
157 }
158 }
159
160 fn visit_string<E>(self, value: String) -> Result<TableId, E>
161 where
162 E: Error,
163 {
164 self.visit_str(&value)
165 }
166 }
167
168 deserializer.deserialize_str(TableIdVisitor)
169 }
170}
171
172impl AsRef<str> for TableId {
173 fn as_ref(&self) -> &str {
174 self.namespace_id.as_str()
179 }
180}
181
182impl AsRef<[u8]> for TableId {
187 fn as_ref(&self) -> &[u8] {
188 self.namespace_id.as_str().as_bytes()
193 }
194}
195
196impl fmt::Display for TableId {
197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198 write!(f, "{}:{}", self.namespace_id, self.table_name)
199 }
200}
201
202impl StorageKey for TableId {
203 fn storage_key(&self) -> Vec<u8> {
204 self.as_storage_key()
205 }
206
207 fn from_storage_key(bytes: &[u8]) -> Result<Self, String> {
208 Self::from_storage_key(bytes).ok_or_else(|| "Invalid TableId format".to_string())
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 #[test]
217 fn test_table_id_new() {
218 let namespace_id = NamespaceId::new("ns1");
219 let table_name = TableName::new("users");
220 let table_id = TableId::new(namespace_id.clone(), table_name.clone());
221
222 assert_eq!(table_id.namespace_id(), &namespace_id);
223 assert_eq!(table_id.table_name(), &table_name);
224 }
225
226 #[test]
227 fn test_table_id_from_strings() {
228 let table_id = TableId::from_strings("ns1", "users");
229 assert_eq!(table_id.namespace_id().as_str(), "ns1");
230 assert_eq!(table_id.table_name().as_str(), "users");
231 }
232
233 #[test]
234 fn test_table_id_try_from_strings() {
235 let table_id = TableId::try_from_strings("ns1", "users").unwrap();
236 assert_eq!(table_id.namespace_id().as_str(), "ns1");
237 assert_eq!(table_id.table_name().as_str(), "users");
238 }
239
240 #[test]
241 fn test_table_id_try_from_strings_invalid() {
242 let err = TableId::try_from_strings("../ns1", "users").unwrap_err();
243 assert!(err.contains("invalid namespace_id"));
244 }
245
246 #[test]
247 fn test_table_id_as_storage_key() {
248 let table_id = TableId::from_strings("ns1", "users");
249 let key = table_id.as_storage_key();
250 assert!(!key.is_empty());
251 let parsed = TableId::from_storage_key(&key).unwrap();
252 assert_eq!(parsed, table_id);
253 }
254
255 #[test]
256 fn test_table_id_from_storage_key() {
257 let key = TableId::from_strings("ns1", "users").as_storage_key();
258 let table_id = TableId::from_storage_key(&key).unwrap();
259
260 assert_eq!(table_id.namespace_id().as_str(), "ns1");
261 assert_eq!(table_id.table_name().as_str(), "users");
262 }
263
264 #[test]
265 fn test_table_id_roundtrip() {
266 let original = TableId::from_strings("ns1", "users");
267 let key = original.as_storage_key();
268 let parsed = TableId::from_storage_key(&key).unwrap();
269
270 assert_eq!(original, parsed);
271 }
272
273 #[test]
274 fn test_table_id_display() {
275 let table_id = TableId::from_strings("ns1", "users");
276 assert_eq!(format!("{}", table_id), "ns1:users");
277 }
278
279 #[test]
280 fn test_table_id_full_name() {
281 let table_id = TableId::from_strings("app", "messages");
282 assert_eq!(table_id.full_name(), "app.messages");
283
284 let table_id2 = TableId::from_strings("my_namespace", "user_table");
285 assert_eq!(table_id2.full_name(), "my_namespace.user_table");
286 }
287
288 #[test]
289 fn test_table_id_serialization() {
290 let table_id = TableId::from_strings("ns1", "users");
291 let json = serde_json::to_string(&table_id).unwrap();
292 let deserialized: TableId = serde_json::from_str(&json).unwrap();
293 assert_eq!(table_id, deserialized);
294 }
295
296 #[test]
297 fn test_table_id_into_parts() {
298 let table_id = TableId::from_strings("ns1", "users");
299 let (namespace_id, table_name) = table_id.into_parts();
300
301 assert_eq!(namespace_id.as_str(), "ns1");
302 assert_eq!(table_name.as_str(), "users");
303 }
304
305 #[test]
306 fn test_table_id_with_underscore_namespace() {
307 let table_id = TableId::try_from_strings("my_namespace", "table_name").unwrap();
308 let key = table_id.as_storage_key();
309 let parsed = TableId::from_storage_key(&key).unwrap();
310
311 assert_eq!(table_id, parsed);
312 }
313}