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