cardinal_sdk/
event_flag.rs

1use bitflags::bitflags;
2bitflags! {
3    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
4    pub struct EventFlag: u32 {
5        const None = objc2_core_services::kFSEventStreamEventFlagNone;
6        const MustScanSubDirs = objc2_core_services::kFSEventStreamEventFlagMustScanSubDirs;
7        const UserDropped = objc2_core_services::kFSEventStreamEventFlagUserDropped;
8        const KernelDropped = objc2_core_services::kFSEventStreamEventFlagKernelDropped;
9        const EventIdsWrapped = objc2_core_services::kFSEventStreamEventFlagEventIdsWrapped;
10        const HistoryDone = objc2_core_services::kFSEventStreamEventFlagHistoryDone;
11        const RootChanged = objc2_core_services::kFSEventStreamEventFlagRootChanged;
12        const Mount = objc2_core_services::kFSEventStreamEventFlagMount;
13        const Unmount = objc2_core_services::kFSEventStreamEventFlagUnmount;
14        const ItemCreated = objc2_core_services::kFSEventStreamEventFlagItemCreated;
15        const ItemRemoved = objc2_core_services::kFSEventStreamEventFlagItemRemoved;
16        const ItemInodeMetaMod = objc2_core_services::kFSEventStreamEventFlagItemInodeMetaMod;
17        const ItemRenamed = objc2_core_services::kFSEventStreamEventFlagItemRenamed;
18        const ItemModified = objc2_core_services::kFSEventStreamEventFlagItemModified;
19        const ItemFinderInfoMod = objc2_core_services::kFSEventStreamEventFlagItemFinderInfoMod;
20        const ItemChangeOwner = objc2_core_services::kFSEventStreamEventFlagItemChangeOwner;
21        const ItemXattrMod = objc2_core_services::kFSEventStreamEventFlagItemXattrMod;
22        const ItemIsFile = objc2_core_services::kFSEventStreamEventFlagItemIsFile;
23        const ItemIsDir = objc2_core_services::kFSEventStreamEventFlagItemIsDir;
24        const ItemIsSymlink = objc2_core_services::kFSEventStreamEventFlagItemIsSymlink;
25        const OwnEvent = objc2_core_services::kFSEventStreamEventFlagOwnEvent;
26        const IsHardlink = objc2_core_services::kFSEventStreamEventFlagItemIsHardlink;
27        const IsLastHardlink = objc2_core_services::kFSEventStreamEventFlagItemIsLastHardlink;
28        const Cloned = objc2_core_services::kFSEventStreamEventFlagItemCloned;
29    }
30}
31
32pub enum EventType {
33    Unknown,
34    File,
35    Dir,
36    Symlink,
37    Hardlink,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum ScanType {
42    /// Scan a single node
43    SingleNode,
44    /// Scan the whole folder, including sub-folders.
45    Folder,
46    /// Something wrong happened, do re-indexing.
47    /// Should only happen with `kFSEventStreamCreateFlagWatchRoot` set in EventStream::new().
48    ReScan,
49    /// Do nothing, since event id is always updated.
50    Nop,
51}
52
53impl EventFlag {
54    pub fn event_type(&self) -> EventType {
55        if self.contains(EventFlag::IsHardlink) | self.contains(EventFlag::IsLastHardlink) {
56            EventType::Hardlink
57        } else if self.contains(EventFlag::ItemIsSymlink) {
58            EventType::Symlink
59        } else if self.contains(EventFlag::ItemIsDir) {
60            EventType::Dir
61        } else if self.contains(EventFlag::ItemIsFile) {
62            EventType::File
63        } else {
64            EventType::Unknown
65        }
66    }
67
68    pub fn scan_type(&self) -> ScanType {
69        let event_type = self.event_type();
70        let is_dir = matches!(event_type, EventType::Dir);
71        if self.contains(EventFlag::HistoryDone) | self.contains(EventFlag::EventIdsWrapped) {
72            ScanType::Nop
73        } else if self.contains(EventFlag::RootChanged) {
74            ScanType::ReScan
75        } else {
76            // Strange event, doesn't know when it happens, processing it using a generic way
77            // e.g. new event: fs_event=FsEvent { path: "/.docid/16777229/changed/782/src=0,dst=41985052", flag: kFSEventStreamEventFlagNone, id: 471533015 }
78            if is_dir {
79                ScanType::Folder
80            } else {
81                ScanType::SingleNode
82            }
83        }
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn test_event_type_deduction() {
93        assert!(matches!(
94            (EventFlag::ItemIsFile).event_type(),
95            EventType::File
96        ));
97        assert!(matches!(
98            (EventFlag::ItemIsDir).event_type(),
99            EventType::Dir
100        ));
101        assert!(matches!(
102            (EventFlag::ItemIsSymlink).event_type(),
103            EventType::Symlink
104        ));
105        assert!(matches!(
106            (EventFlag::IsHardlink).event_type(),
107            EventType::Hardlink
108        ));
109        // Unknown when no type bits set
110        assert!(matches!((EventFlag::None).event_type(), EventType::Unknown));
111    }
112
113    #[test]
114    fn test_scan_type_root_changed_and_history_done() {
115        // RootChanged should always trigger ReScan regardless of type bits; test with RootChanged only.
116        assert_eq!(EventFlag::RootChanged.scan_type(), ScanType::ReScan);
117        assert_eq!(EventFlag::HistoryDone.scan_type(), ScanType::Nop);
118    }
119
120    #[test]
121    fn test_scan_type_created_removed_modified() {
122        // File create => SingleNode
123        assert!(matches!(
124            (EventFlag::ItemCreated | EventFlag::ItemIsFile).scan_type(),
125            ScanType::SingleNode
126        ));
127        // Dir removal => Folder
128        assert!(matches!(
129            (EventFlag::ItemRemoved | EventFlag::ItemIsDir).scan_type(),
130            ScanType::Folder
131        ));
132        // File removal => SingleNode
133        assert!(matches!(
134            (EventFlag::ItemRemoved | EventFlag::ItemIsFile).scan_type(),
135            ScanType::SingleNode
136        ));
137        // File modified => SingleNode
138        assert!(matches!(
139            (EventFlag::ItemModified | EventFlag::ItemIsFile).scan_type(),
140            ScanType::SingleNode
141        ));
142    }
143
144    #[test]
145    fn test_scan_type_must_scan_subdirs() {
146        // MustScanSubDirs => Folder
147        assert!(matches!(
148            (EventFlag::MustScanSubDirs | EventFlag::ItemIsDir).scan_type(),
149            ScanType::Folder
150        ));
151    }
152}