1#[cfg(feature = "serde-support")]
22use serde::{Deserialize, Serialize};
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))]
31#[cfg_attr(feature = "serde-support", serde(rename_all = "lowercase"))]
32pub enum Lifecycle {
33 Spec,
34 Data,
35 Ownership,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq, Hash)]
44#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))]
45#[cfg_attr(feature = "serde-support", serde(untagged))]
46pub enum Version {
47 Counter(u64),
48 Cid(String),
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
67#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))]
68pub struct BaseChange<T> {
69 pub structure: String,
70 pub version: Version,
71 pub t_ns: u64,
72 #[cfg_attr(
73 feature = "serde-support",
74 serde(default, skip_serializing_if = "Option::is_none")
75 )]
76 pub seq: Option<u64>,
77 pub lifecycle: Lifecycle,
78 pub change: T,
79}
80
81#[derive(Debug, Clone, PartialEq, Eq)]
87#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))]
88#[cfg_attr(
89 feature = "serde-support",
90 serde(tag = "kind", rename_all = "camelCase")
91)]
92pub enum LogChange<T> {
93 Append { value: T },
94 AppendMany { values: Vec<T> },
95 Clear { count: usize },
96 TrimHead { n: usize },
97}
98
99#[derive(Debug, Clone, PartialEq, Eq)]
101#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))]
102#[cfg_attr(
103 feature = "serde-support",
104 serde(tag = "kind", rename_all = "camelCase")
105)]
106pub enum ListChange<T> {
107 Append { value: T },
108 AppendMany { values: Vec<T> },
109 Insert { index: usize, value: T },
110 InsertMany { index: usize, values: Vec<T> },
111 Pop { index: i64, value: T },
112 Clear { count: usize },
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
117#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))]
118#[cfg_attr(feature = "serde-support", serde(rename_all = "camelCase"))]
119pub enum DeleteReason {
120 Explicit,
121 Expired,
122 LruEvict,
123 Archived,
124}
125
126#[derive(Debug, Clone, PartialEq, Eq)]
128#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))]
129#[cfg_attr(
130 feature = "serde-support",
131 serde(tag = "kind", rename_all = "camelCase")
132)]
133pub enum MapChange<K, V> {
134 Set {
135 key: K,
136 value: V,
137 },
138 Delete {
139 key: K,
140 previous: V,
141 reason: DeleteReason,
142 },
143 Clear {
144 count: usize,
145 },
146}
147
148#[derive(Debug, Clone, PartialEq, Eq)]
150#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))]
151#[cfg_attr(
152 feature = "serde-support",
153 serde(tag = "kind", rename_all = "camelCase")
154)]
155pub enum IndexChange<K, V> {
156 Upsert {
157 primary: K,
158 secondary: String,
159 value: V,
160 },
161 Delete {
162 primary: K,
163 },
164 DeleteMany {
165 primaries: Vec<K>,
166 },
167 Clear {
168 count: usize,
169 },
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[cfg(feature = "serde-support")]
177 #[test]
178 fn lifecycle_serde_lowercase() {
179 assert_eq!(serde_json::to_string(&Lifecycle::Spec).unwrap(), "\"spec\"");
180 assert_eq!(serde_json::to_string(&Lifecycle::Data).unwrap(), "\"data\"");
181 assert_eq!(
182 serde_json::to_string(&Lifecycle::Ownership).unwrap(),
183 "\"ownership\""
184 );
185 let parsed: Lifecycle = serde_json::from_str("\"data\"").unwrap();
186 assert_eq!(parsed, Lifecycle::Data);
187 }
188
189 #[cfg(feature = "serde-support")]
190 #[test]
191 fn version_serde_untagged_matches_ts_number_or_string() {
192 assert_eq!(serde_json::to_string(&Version::Counter(42)).unwrap(), "42");
194 assert_eq!(
196 serde_json::to_string(&Version::Cid("bafy123".into())).unwrap(),
197 "\"bafy123\""
198 );
199 let parsed_num: Version = serde_json::from_str("7").unwrap();
201 assert_eq!(parsed_num, Version::Counter(7));
202 let parsed_str: Version = serde_json::from_str("\"bafyabc\"").unwrap();
203 assert_eq!(parsed_str, Version::Cid("bafyabc".into()));
204 }
205
206 #[cfg(feature = "serde-support")]
207 #[test]
208 fn base_change_skips_optional_seq() {
209 let c: BaseChange<u64> = BaseChange {
210 structure: "test".into(),
211 version: Version::Counter(1),
212 t_ns: 100,
213 seq: None,
214 lifecycle: Lifecycle::Data,
215 change: 99,
216 };
217 let s = serde_json::to_string(&c).unwrap();
218 assert!(
219 !s.contains("seq"),
220 "Option::None should not emit seq field: {s}"
221 );
222 }
223}