1use std::ffi::OsString;
2
3use clap::{Arg, ArgAction, Command};
4use inotify::WatchMask;
5
6pub struct Anotify {
9 pub mask: WatchMask,
11
12 pub regex: Option<String>,
14
15 pub recursive: bool,
17
18 pub targets: Vec<OsString>,
20}
21
22fn app() -> Command {
23 let cmd = Command::new("anotify")
24 .author("CC")
25 .arg(
26 Arg::new("access")
27 .long("access")
28 .short('a')
29 .action(ArgAction::SetTrue)
30 .help("File was accessed"),
31 )
32 .arg(
33 Arg::new("attrib")
34 .long("attrib")
35 .short('A')
36 .action(ArgAction::SetTrue)
37 .help("Metadata (permissions, timestamps, …) changed"),
38 )
39 .arg(
40 Arg::new("close_write")
41 .long("close_write")
42 .short('x')
43 .action(ArgAction::SetTrue)
44 .help("File opened for writing was closed"),
45 )
46 .arg(
47 Arg::new("close_nowrite")
48 .long("close_nowrite")
49 .short('q')
50 .action(ArgAction::SetTrue)
51 .help("File or directory not opened for writing was closed"),
52 )
53 .arg(
54 Arg::new("create")
55 .long("create")
56 .short('c')
57 .action(ArgAction::SetTrue)
58 .help("File/directory created in watched directory"),
59 )
60 .arg(
61 Arg::new("delete")
62 .long("delete")
63 .short('d')
64 .action(ArgAction::SetTrue)
65 .help("File/directory deleted from watched directory"),
66 )
67 .arg(
68 Arg::new("delete_self")
69 .long("delete_self")
70 .short('D')
71 .action(ArgAction::SetTrue)
72 .help("Watched file/directory was deleted"),
73 )
74 .arg(
75 Arg::new("modify")
76 .long("modify")
77 .short('m')
78 .action(ArgAction::SetTrue)
79 .help("File was modified"),
80 )
81 .arg(
82 Arg::new("move_self")
83 .long("move_self")
84 .short('S')
85 .action(ArgAction::SetTrue)
86 .help("Watched file/directory was moved"),
87 )
88 .arg(
89 Arg::new("moved_from")
90 .long("moved_from")
91 .short('F')
92 .action(ArgAction::SetTrue)
93 .help("File was renamed/moved; watched directory contained old name"),
94 )
95 .arg(
96 Arg::new("moved_to")
97 .long("moved_to")
98 .short('T')
99 .action(ArgAction::SetTrue)
100 .help("File was renamed/moved; watched directory contains new name"),
101 )
102 .arg(
103 Arg::new("open")
104 .long("open")
105 .short('o')
106 .action(ArgAction::SetTrue)
107 .help("File or directory was opened"),
108 )
109 .arg(
110 Arg::new("move")
111 .long("move")
112 .short('M')
113 .action(ArgAction::SetTrue)
114 .help("Watch for all move events"),
115 )
116 .arg(
117 Arg::new("close")
118 .long("close")
119 .short('Q')
120 .action(ArgAction::SetTrue)
121 .help("Watch for all close events"),
122 )
123 .arg(
124 Arg::new("all")
125 .long("all")
126 .action(ArgAction::SetTrue)
127 .help("Watch for all events"),
128 )
129 .arg(
130 Arg::new("dont_follow")
131 .long("dont_follow")
132 .action(ArgAction::SetTrue)
133 .help("Don’t dereference the path if it is a symbolic link"),
134 )
135 .arg(
136 Arg::new("excl_unlink")
137 .long("excl_unlink")
138 .action(ArgAction::SetTrue)
139 .help("Filter events for directory entries that have been unlinked"),
140 )
141 .arg(
142 Arg::new("mask_add")
143 .long("mask_add")
144 .action(ArgAction::SetTrue)
145 .help("If a watch for the inode exists, amend it instead of replacing it"),
146 )
147 .arg(
148 Arg::new("oneshot")
149 .long("oneshot")
150 .action(ArgAction::SetTrue)
151 .help("Only receive one event, then remove the watch"),
152 )
153 .arg(
154 Arg::new("dir")
155 .long("dir")
156 .action(ArgAction::SetTrue)
157 .help("Only watch path, if it is a directory"),
158 )
159 .arg(
160 Arg::new("recursive")
161 .short('R')
162 .long("recursive")
163 .action(ArgAction::SetTrue)
164 .help("Recursive monitor a path"),
165 )
166 .arg(
167 Arg::new("regex")
168 .short('E')
169 .long("regex")
170 .action(ArgAction::Set)
171 .help("Use regex to match file name, only matched will report"),
172 )
173 .arg(
174 Arg::new("target")
175 .action(ArgAction::Append)
176 .default_value("./"),
177 );
178
179 cmd
180}
181
182pub fn parse() -> crate::Result<Anotify> {
183 let _args = app().get_matches();
184 let mut mask = WatchMask::empty();
185
186 if *_args.get_one::<bool>("access").unwrap() {
188 mask |= WatchMask::ACCESS;
189 }
190
191 if *_args.get_one::<bool>("attrib").unwrap() {
193 mask |= WatchMask::ATTRIB;
194 }
195
196 if *_args.get_one::<bool>("close_write").unwrap() {
198 mask |= WatchMask::CLOSE_WRITE;
199 }
200
201 if *_args.get_one::<bool>("close_nowrite").unwrap() {
203 mask |= WatchMask::CLOSE_NOWRITE;
204 }
205
206 if *_args.get_one::<bool>("create").unwrap() {
208 mask |= WatchMask::CREATE;
209 }
210
211 if *_args.get_one::<bool>("delete").unwrap() {
213 mask |= WatchMask::DELETE;
214 }
215
216 if *_args.get_one::<bool>("delete_self").unwrap() {
218 mask |= WatchMask::DELETE_SELF;
219 }
220
221 if *_args.get_one::<bool>("modify").unwrap() {
223 mask |= WatchMask::MODIFY;
224 }
225
226 if *_args.get_one::<bool>("move_self").unwrap() {
228 mask |= WatchMask::MOVE_SELF;
229 }
230
231 if *_args.get_one::<bool>("moved_from").unwrap() {
233 mask |= WatchMask::MOVED_FROM;
234 }
235
236 if *_args.get_one::<bool>("moved_to").unwrap() {
238 mask |= WatchMask::MOVED_TO;
239 }
240
241 if *_args.get_one::<bool>("open").unwrap() {
243 mask |= WatchMask::OPEN;
244 }
245
246 if *_args.get_one::<bool>("all").unwrap() {
248 mask |= WatchMask::ALL_EVENTS;
249 }
250
251 if *_args.get_one::<bool>("move").unwrap() {
253 mask |= WatchMask::MOVE;
254 }
255
256 if *_args.get_one::<bool>("close").unwrap() {
258 mask |= WatchMask::CLOSE;
259 }
260
261 if *_args.get_one::<bool>("dont_follow").unwrap() {
263 mask |= WatchMask::DONT_FOLLOW;
264 }
265
266 if *_args.get_one::<bool>("excl_unlink").unwrap() {
268 mask |= WatchMask::EXCL_UNLINK;
269 }
270
271 if *_args.get_one::<bool>("mask_add").unwrap() {
273 mask |= WatchMask::MASK_ADD;
274 }
275
276 if *_args.get_one::<bool>("oneshot").unwrap() {
278 mask |= WatchMask::ONESHOT;
279 }
280
281 if *_args.get_one::<bool>("dir").unwrap() {
283 mask |= WatchMask::ONLYDIR;
284 }
285
286 if mask.is_empty() {
287 return Err("Error: You must point at least one EVENT".into());
288 }
289
290 let mut recursive = false;
291 if *_args.get_one::<bool>("recursive").unwrap() {
292 recursive = true;
293 }
294
295 let mut regex = None;
296 if let Some(_regex) = _args.get_one::<String>("regex") {
297 regex = Some(String::from(_regex));
298 }
299
300 let mut targets: Vec<OsString> = vec![];
301 let mut target = _args.get_many::<String>("target").unwrap();
302 while let Some(_target) = target.next() {
303 targets.push(_target.into());
304 }
305
306 Ok(Anotify {
307 mask,
308 recursive,
309 regex,
310 targets,
311 })
312}