1use serde::{Deserialize, Serialize};
4use std::fmt;
5
6#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
8pub struct RecordKey {
9 pub namespace: String,
10 pub collection: String,
11 pub id: String,
12}
13
14impl RecordKey {
15 pub fn new(
16 namespace: impl Into<String>,
17 collection: impl Into<String>,
18 id: impl Into<String>,
19 ) -> Self {
20 Self {
21 namespace: namespace.into(),
22 collection: collection.into(),
23 id: id.into(),
24 }
25 }
26}
27
28impl fmt::Display for RecordKey {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 write!(f, "{}/{}/{}", self.namespace, self.collection, self.id)
31 }
32}
33
34#[derive(Clone, Debug, Default, PartialEq, Eq)]
36pub struct KeyPrefix {
37 pub namespace: Option<String>,
38 pub collection: Option<String>,
39}
40
41impl KeyPrefix {
42 pub fn matches(&self, key: &RecordKey) -> bool {
43 self.namespace.as_ref().is_none_or(|n| n == &key.namespace)
44 && self
45 .collection
46 .as_ref()
47 .is_none_or(|c| c == &key.collection)
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54
55 #[test]
56 fn display_is_slash_joined() {
57 let k = RecordKey::new("caliban", "topics", "rust-tips");
58 assert_eq!(k.to_string(), "caliban/topics/rust-tips");
59 }
60
61 #[test]
62 fn prefix_matches_on_set_fields_only() {
63 let k = RecordKey::new("caliban", "topics", "x");
64 assert!(
65 KeyPrefix {
66 namespace: Some("caliban".into()),
67 collection: None
68 }
69 .matches(&k)
70 );
71 assert!(
72 !KeyPrefix {
73 namespace: Some("other".into()),
74 collection: None
75 }
76 .matches(&k)
77 );
78 assert!(KeyPrefix::default().matches(&k));
79 }
80}