rust_ef/
entity_snapshot.rs1use crate::provider::DbValue;
9
10#[derive(Debug, Clone)]
16pub struct EntitySnapshot {
17 entries: Box<[(&'static str, DbValue)]>,
18}
19
20impl EntitySnapshot {
21 pub fn new(entries: Vec<(&'static str, DbValue)>) -> Self {
23 Self {
24 entries: entries.into_boxed_slice(),
25 }
26 }
27
28 pub fn empty() -> Self {
30 Self {
31 entries: Box::new([]),
32 }
33 }
34
35 pub fn get(&self, field: &str) -> Option<&DbValue> {
37 self.entries
38 .iter()
39 .find(|(k, _)| *k == field)
40 .map(|(_, v)| v)
41 }
42
43 pub fn iter(&self) -> impl Iterator<Item = (&'static str, &DbValue)> {
45 self.entries.iter().map(|(k, v)| (*k, v))
46 }
47
48 pub fn len(&self) -> usize {
50 self.entries.len()
51 }
52
53 pub fn is_empty(&self) -> bool {
55 self.entries.is_empty()
56 }
57}
58
59impl PartialEq for EntitySnapshot {
60 fn eq(&self, other: &Self) -> bool {
62 self.entries.len() == other.entries.len()
63 && self.entries.iter().all(|(k, v)| other.get(k) == Some(v))
64 }
65}
66
67impl From<Vec<(&'static str, DbValue)>> for EntitySnapshot {
68 fn from(entries: Vec<(&'static str, DbValue)>) -> Self {
69 Self::new(entries)
70 }
71}
72
73impl Default for EntitySnapshot {
74 fn default() -> Self {
75 Self::empty()
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn construct_and_get() {
85 let snap = EntitySnapshot::new(vec![
86 ("id", DbValue::I32(42)),
87 ("name", DbValue::String("hello".into())),
88 ]);
89 assert_eq!(snap.get("id"), Some(&DbValue::I32(42)));
90 assert_eq!(snap.get("name"), Some(&DbValue::String("hello".into())));
91 assert_eq!(snap.get("missing"), None);
92 }
93
94 #[test]
95 fn empty_snapshot() {
96 let snap = EntitySnapshot::empty();
97 assert!(snap.is_empty());
98 assert_eq!(snap.len(), 0);
99 assert_eq!(snap.get("anything"), None);
100 }
101
102 #[test]
103 fn iter_all_fields() {
104 let snap = EntitySnapshot::new(vec![("a", DbValue::I32(1)), ("b", DbValue::I32(2))]);
105 let mut fields: Vec<&str> = snap.iter().map(|(k, _)| k).collect();
106 fields.sort();
107 assert_eq!(fields, vec!["a", "b"]);
108 }
109
110 #[test]
111 fn eq_order_independent() {
112 let a = EntitySnapshot::new(vec![
113 ("id", DbValue::I32(1)),
114 ("name", DbValue::String("x".into())),
115 ]);
116 let b = EntitySnapshot::new(vec![
117 ("name", DbValue::String("x".into())),
118 ("id", DbValue::I32(1)),
119 ]);
120 assert_eq!(a, b);
121 }
122
123 #[test]
124 fn ne_different_values() {
125 let a = EntitySnapshot::new(vec![("id", DbValue::I32(1))]);
126 let b = EntitySnapshot::new(vec![("id", DbValue::I32(2))]);
127 assert_ne!(a, b);
128 }
129
130 #[test]
131 fn ne_different_lengths() {
132 let a = EntitySnapshot::new(vec![("id", DbValue::I32(1))]);
133 let b = EntitySnapshot::new(vec![
134 ("id", DbValue::I32(1)),
135 ("name", DbValue::String("x".into())),
136 ]);
137 assert_ne!(a, b);
138 }
139
140 #[test]
141 fn clone_preserves_data() {
142 let snap = EntitySnapshot::new(vec![
143 ("id", DbValue::I64(99)),
144 ("active", DbValue::Bool(true)),
145 ]);
146 let cloned = snap.clone();
147 assert_eq!(snap, cloned);
148 }
149
150 #[test]
151 fn default_is_empty() {
152 let snap = EntitySnapshot::default();
153 assert!(snap.is_empty());
154 }
155}