1use std::collections::HashSet;
2use std::fmt;
3use std::hash::{Hash, Hasher};
4use std::iter::FromIterator;
5use std::ops::{BitOr, Sub};
6use std::path::PathBuf;
7use std::str::FromStr;
8
9use derive_more::{Deref, DerefMut, IntoIterator};
10use serde::{Deserialize, Serialize};
11use strum::{EnumString, EnumVariantNames, VariantNames};
12
13#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(rename_all = "snake_case", deny_unknown_fields)]
16pub struct Change {
17 pub timestamp: u64,
20
21 pub kind: ChangeKind,
23
24 pub path: PathBuf,
26
27 #[serde(default, skip_serializing_if = "ChangeDetails::is_empty")]
29 pub details: ChangeDetails,
30}
31
32#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
34#[serde(default, rename_all = "snake_case", deny_unknown_fields)]
35pub struct ChangeDetails {
36 #[serde(skip_serializing_if = "Option::is_none")]
38 pub attribute: Option<ChangeDetailsAttribute>,
39
40 #[serde(skip_serializing_if = "Option::is_none")]
43 pub renamed: Option<PathBuf>,
44
45 #[serde(skip_serializing_if = "Option::is_none")]
51 pub timestamp: Option<u64>,
52
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub extra: Option<String>,
56}
57
58impl ChangeDetails {
59 pub fn is_empty(&self) -> bool {
61 self.attribute.is_none()
62 && self.renamed.is_none()
63 && self.timestamp.is_none()
64 && self.extra.is_none()
65 }
66}
67
68#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
70#[serde(rename_all = "snake_case", deny_unknown_fields)]
71pub enum ChangeDetailsAttribute {
72 Ownership,
73 Permissions,
74 Timestamp,
75}
76
77#[derive(
81 Copy,
82 Clone,
83 Debug,
84 strum::Display,
85 EnumString,
86 EnumVariantNames,
87 Hash,
88 PartialEq,
89 Eq,
90 PartialOrd,
91 Ord,
92 Serialize,
93 Deserialize,
94)]
95#[serde(rename_all = "snake_case", deny_unknown_fields)]
96#[strum(serialize_all = "snake_case")]
97pub enum ChangeKind {
98 Access,
100
101 Attribute,
103
104 CloseWrite,
106
107 CloseNoWrite,
109
110 Create,
112
113 Delete,
115
116 Modify,
118
119 Open,
121
122 Rename,
124
125 Unknown,
127}
128
129impl ChangeKind {
130 pub const fn variants() -> &'static [&'static str] {
132 Self::VARIANTS
133 }
134
135 pub fn all() -> Vec<ChangeKind> {
137 ChangeKindSet::all().into_sorted_vec()
138 }
139
140 pub fn is_access(&self) -> bool {
142 matches!(
143 self,
144 Self::Access | Self::CloseWrite | Self::CloseNoWrite | Self::Open
145 )
146 }
147
148 pub fn is_create(&self) -> bool {
150 matches!(self, Self::Create)
151 }
152
153 pub fn is_delete(&self) -> bool {
155 matches!(self, Self::Delete)
156 }
157
158 pub fn is_modify(&self) -> bool {
160 matches!(self, Self::Attribute | Self::Modify)
161 }
162
163 pub fn is_rename(&self) -> bool {
165 matches!(self, Self::Rename)
166 }
167
168 pub fn is_unknown(&self) -> bool {
170 matches!(self, Self::Unknown)
171 }
172}
173
174impl BitOr for ChangeKind {
175 type Output = ChangeKindSet;
176
177 fn bitor(self, rhs: Self) -> Self::Output {
178 let mut set = ChangeKindSet::empty();
179 set.insert(self);
180 set.insert(rhs);
181 set
182 }
183}
184
185#[derive(Clone, Debug, Deref, DerefMut, IntoIterator, Serialize, Deserialize)]
187pub struct ChangeKindSet(HashSet<ChangeKind>);
188
189impl ChangeKindSet {
190 pub fn new(set: impl IntoIterator<Item = ChangeKind>) -> Self {
191 set.into_iter().collect()
192 }
193
194 pub fn empty() -> Self {
196 Self(HashSet::new())
197 }
198
199 pub fn all() -> Self {
201 vec![
202 ChangeKind::Access,
203 ChangeKind::Attribute,
204 ChangeKind::CloseWrite,
205 ChangeKind::CloseNoWrite,
206 ChangeKind::Create,
207 ChangeKind::Delete,
208 ChangeKind::Modify,
209 ChangeKind::Open,
210 ChangeKind::Rename,
211 ChangeKind::Unknown,
212 ]
213 .into_iter()
214 .collect()
215 }
216
217 pub fn into_sorted_vec(self) -> Vec<ChangeKind> {
219 let mut v = self.0.into_iter().collect::<Vec<_>>();
220 v.sort();
221 v
222 }
223}
224
225impl fmt::Display for ChangeKindSet {
226 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
229 let mut kinds = self
230 .0
231 .iter()
232 .map(ToString::to_string)
233 .collect::<Vec<String>>();
234 kinds.sort_unstable();
235 write!(f, "{}", kinds.join(","))
236 }
237}
238
239impl PartialEq for ChangeKindSet {
240 fn eq(&self, other: &Self) -> bool {
241 self.to_string() == other.to_string()
242 }
243}
244
245impl Eq for ChangeKindSet {}
246
247impl Hash for ChangeKindSet {
248 fn hash<H: Hasher>(&self, state: &mut H) {
250 self.to_string().hash(state);
251 }
252}
253
254impl BitOr<ChangeKindSet> for ChangeKindSet {
255 type Output = Self;
256
257 fn bitor(mut self, rhs: ChangeKindSet) -> Self::Output {
258 self.extend(rhs.0);
259 self
260 }
261}
262
263impl BitOr<ChangeKind> for ChangeKindSet {
264 type Output = Self;
265
266 fn bitor(mut self, rhs: ChangeKind) -> Self::Output {
267 self.0.insert(rhs);
268 self
269 }
270}
271
272impl BitOr<ChangeKindSet> for ChangeKind {
273 type Output = ChangeKindSet;
274
275 fn bitor(self, rhs: ChangeKindSet) -> Self::Output {
276 rhs | self
277 }
278}
279
280impl Sub<ChangeKindSet> for ChangeKindSet {
281 type Output = Self;
282
283 fn sub(self, other: Self) -> Self::Output {
284 ChangeKindSet(&self.0 - &other.0)
285 }
286}
287
288impl Sub<&'_ ChangeKindSet> for &ChangeKindSet {
289 type Output = ChangeKindSet;
290
291 fn sub(self, other: &ChangeKindSet) -> Self::Output {
292 ChangeKindSet(&self.0 - &other.0)
293 }
294}
295
296impl FromStr for ChangeKindSet {
297 type Err = strum::ParseError;
298
299 fn from_str(s: &str) -> Result<Self, Self::Err> {
300 let mut change_set = HashSet::new();
301
302 for word in s.split(',') {
303 change_set.insert(ChangeKind::from_str(word.trim())?);
304 }
305
306 Ok(ChangeKindSet(change_set))
307 }
308}
309
310impl FromIterator<ChangeKind> for ChangeKindSet {
311 fn from_iter<I: IntoIterator<Item = ChangeKind>>(iter: I) -> Self {
312 let mut change_set = HashSet::new();
313
314 for i in iter {
315 change_set.insert(i);
316 }
317
318 ChangeKindSet(change_set)
319 }
320}
321
322impl From<ChangeKind> for ChangeKindSet {
323 fn from(change_kind: ChangeKind) -> Self {
324 let mut set = Self::empty();
325 set.insert(change_kind);
326 set
327 }
328}
329
330impl From<Vec<ChangeKind>> for ChangeKindSet {
331 fn from(changes: Vec<ChangeKind>) -> Self {
332 changes.into_iter().collect()
333 }
334}
335
336impl Default for ChangeKindSet {
337 fn default() -> Self {
338 Self::empty()
339 }
340}
341
342#[cfg(test)]
343mod tests {
344 use super::*;
345
346 mod change_kind_set {
347 use super::*;
348
349 #[test]
350 fn should_be_able_to_serialize_to_json() {
351 let set = ChangeKindSet::new([ChangeKind::CloseWrite]);
352
353 let value = serde_json::to_value(set).unwrap();
354 assert_eq!(value, serde_json::json!(["close_write"]));
355 }
356
357 #[test]
358 fn should_be_able_to_deserialize_from_json() {
359 let value = serde_json::json!(["close_write"]);
360
361 let set: ChangeKindSet = serde_json::from_value(value).unwrap();
362 assert_eq!(set, ChangeKindSet::new([ChangeKind::CloseWrite]));
363 }
364
365 #[test]
366 fn should_be_able_to_serialize_to_msgpack() {
367 let set = ChangeKindSet::new([ChangeKind::CloseWrite]);
368
369 let _ = rmp_serde::encode::to_vec_named(&set).unwrap();
374 }
375
376 #[test]
377 fn should_be_able_to_deserialize_from_msgpack() {
378 let buf =
383 rmp_serde::encode::to_vec_named(&ChangeKindSet::new([ChangeKind::CloseWrite]))
384 .unwrap();
385
386 let set: ChangeKindSet = rmp_serde::decode::from_slice(&buf).unwrap();
387 assert_eq!(set, ChangeKindSet::new([ChangeKind::CloseWrite]));
388 }
389 }
390
391 mod change_kind {
392 use super::*;
393
394 #[test]
395 fn should_be_able_to_serialize_to_json() {
396 let kind = ChangeKind::CloseWrite;
397
398 let value = serde_json::to_value(kind).unwrap();
399 assert_eq!(value, serde_json::json!("close_write"));
400 }
401
402 #[test]
403 fn should_be_able_to_deserialize_from_json() {
404 let value = serde_json::json!("close_write");
405
406 let kind: ChangeKind = serde_json::from_value(value).unwrap();
407 assert_eq!(kind, ChangeKind::CloseWrite);
408 }
409
410 #[test]
411 fn should_be_able_to_serialize_to_msgpack() {
412 let kind = ChangeKind::CloseWrite;
413
414 let _ = rmp_serde::encode::to_vec_named(&kind).unwrap();
419 }
420
421 #[test]
422 fn should_be_able_to_deserialize_from_msgpack() {
423 let buf = rmp_serde::encode::to_vec_named(&ChangeKind::CloseWrite).unwrap();
428
429 let kind: ChangeKind = rmp_serde::decode::from_slice(&buf).unwrap();
430 assert_eq!(kind, ChangeKind::CloseWrite);
431 }
432 }
433}