compyrs/
lib.rs

1extern crate pgs_files;
2extern crate users;
3
4use hostfile::parse_hostfile;
5use is_executable::IsExecutable;
6use pgs_files::group;
7use servicefile::parse_servicefile;
8use std::collections::HashSet;
9use std::env;
10use std::path::{self, Path};
11use std::fs::{read_dir, ReadDir};
12use users::{all_users, User};
13
14pub struct FilterParams<'a> {
15    pub filter: Option<glob::Pattern>,
16    pub keep_filter: bool,
17    pub input: &'a str,
18    pub prepend: &'a str,
19    pub append: &'a str,
20}
21
22pub fn filter_and_display(completions: impl Iterator<Item = String>, params: &FilterParams) {
23    for entry in completions {
24        if !entry.starts_with(params.input) {
25            continue;
26        }
27
28        let mut keep_entry = true;
29        if let Some(f) = params.filter.as_ref() {
30            keep_entry = params.keep_filter == f.matches(&entry);
31        }
32
33        if keep_entry {
34            println!("{}{}{}", params.prepend, entry, params.append);
35        }
36    }
37}
38
39pub struct PathDirIterator {
40    paths: String,
41    idx: usize,
42}
43
44impl Iterator for PathDirIterator {
45    type Item = String;
46
47    fn next(&mut self) -> Option<String> {
48        let startidx = self.idx;
49        match &self.paths.as_str()[startidx..].find(':') {
50            None => None,
51            Some(idx) => {
52                let endidx = idx + startidx;
53                self.idx = endidx + 1;
54                Some(String::from(&self.paths[startidx..endidx]))
55            }
56        }
57    }
58}
59
60fn path_dir_iterator() -> Option<PathDirIterator> {
61    let path = env::var("PATH");
62    if let Ok(path) = path {
63        return Some(PathDirIterator {
64            paths: path,
65            idx: 0,
66        });
67    }
68
69    None
70}
71
72pub struct PathCompletion {
73    paths: PathDirIterator,
74    dir: Option<ReadDir>,
75}
76
77impl PathCompletion {
78    pub fn new() -> Option<PathCompletion> {
79        match path_dir_iterator() {
80            Some(paths) => Some(PathCompletion { paths, dir: None }),
81            None => None,
82        }
83    }
84
85    fn advance_path(&mut self) {
86        loop {
87            let path = self.paths.next();
88            match path {
89                None => {
90                    self.dir = None;
91                    break;
92                }
93                Some(p) => {
94                    if let Ok(dir) = read_dir(&p) {
95                        self.dir = Some(dir);
96                        break;
97                    }
98
99                    // couldn't open dir, try the next path
100                }
101            }
102        }
103    }
104}
105
106impl Iterator for PathCompletion {
107    type Item = String;
108
109    fn next(&mut self) -> Option<String> {
110        if let None = self.dir {
111            self.advance_path();
112        }
113
114        loop {
115            let entry = self.dir.as_mut().unwrap().next();
116            if let None = entry {
117                self.advance_path();
118                if let None = self.dir {
119                    return None;
120                }
121                continue;
122            }
123
124            let entry = match entry {
125                Some(Ok(entry)) => entry,
126                _ => continue,
127            };
128
129            if !entry.path().is_executable() {
130                continue;
131            }
132
133            return Some(entry.file_name().into_string().unwrap());
134        }
135    }
136}
137
138pub struct DirCompletion {
139    parent: String,
140    dir: ReadDir,
141    search_files: bool,
142    search_dirs: bool,
143    displayed_parent: bool,
144    displayed_curr: bool,
145}
146
147impl Iterator for DirCompletion {
148    type Item = String;
149
150    fn next(&mut self) -> Option<String> {
151        loop {
152            if (self.parent == "." || self.parent == "..") && self.search_dirs {
153                if !self.displayed_curr {
154                    self.displayed_curr = true;
155                    return Some(".".to_string());
156                } else if !self.displayed_parent {
157                    self.displayed_parent = true;
158                    return Some("..".to_string());
159                }
160            }
161
162            if let Some(entry) = self.dir.next() {
163                match entry {
164                    Err(_) => {}
165                    Ok(entry) => {
166                        let is_dir = match entry.metadata() {
167                            Ok(metadata) => metadata.is_dir(),
168                            _ => false,
169                        };
170
171                        if (self.search_dirs && is_dir) || (self.search_files && !is_dir) {
172                            let name = entry.file_name().into_string().unwrap();
173                            if self.parent == "." || self.parent == ".." {
174                                return Some(name);
175                            }
176                            return Some(Path::new(&self.parent).join(name).to_str().unwrap().to_string());
177                        }
178                    }
179                }
180
181                continue;
182            }
183
184            return None;
185        }
186    }
187}
188
189impl DirCompletion {
190    pub fn new(input: &str, search_files: bool, search_dirs: bool) -> Option<DirCompletion> {
191        let parent = {
192            let mut p = Path::new(".");
193            if input.len() != 0 {
194                p = Path::new(input);
195                if path::is_separator(input.chars().last().unwrap()) {
196                    if !p.exists() || !p.is_dir() {
197                        return None;
198                    }
199                } else {
200                    p = p.parent().unwrap_or(Path::new("."));
201                    if p.to_str().unwrap().len() == 0 {
202                        p = Path::new(".");
203                    }
204
205                    if !p.exists() || !p.is_dir() {
206                        return None;
207                    }
208                }
209            }
210            p
211        };
212
213        let mut parent_path = parent.to_str().unwrap();
214        if input == ".." {
215            parent_path = "..";
216        } if !input.starts_with(parent_path) {
217            parent_path = "";
218        }
219        eprintln!("Using parent_path: {} for path {:?}", parent_path, parent);
220
221        if let Ok(dir) = read_dir(parent) {
222            Some(DirCompletion {
223                parent: parent_path.to_string(),
224                dir,
225                search_files,
226                search_dirs,
227                displayed_parent: false,
228                displayed_curr: false,
229            })
230        } else {
231            None
232        }
233    }
234}
235
236pub struct EnvCompletion {
237    vars: env::Vars,
238}
239
240impl Iterator for EnvCompletion {
241    type Item = String;
242
243    fn next(&mut self) -> Option<String> {
244        self.vars.next().map(|v| v.0)
245    }
246}
247
248impl EnvCompletion {
249    pub fn new() -> EnvCompletion {
250        EnvCompletion { vars: env::vars() }
251    }
252}
253
254pub trait Stringify {
255    fn get_string(&self) -> String;
256}
257
258pub struct VecCompletion<T: Stringify> {
259    elements: Vec<T>,
260    idx: usize,
261}
262
263impl<T: Stringify> Iterator for VecCompletion<T> {
264    type Item = String;
265
266    fn next(&mut self) -> Option<String> {
267        loop {
268            if self.idx >= self.elements.len() {
269                return None;
270            }
271
272            let element = &self.elements[self.idx].get_string();
273            self.idx += 1;
274
275            return Some(element.to_string());
276        }
277    }
278}
279
280impl Stringify for group::GroupEntry {
281    fn get_string(&self) -> String {
282        self.name.to_string()
283    }
284}
285
286pub type GroupCompletion = VecCompletion<group::GroupEntry>;
287
288impl GroupCompletion {
289    pub fn new() -> GroupCompletion {
290        GroupCompletion {
291            elements: group::get_all_entries(),
292            idx: 0,
293        }
294    }
295}
296
297impl Stringify for String {
298    fn get_string(&self) -> String {
299        return self.to_string();
300    }
301}
302
303pub type HostCompletion = VecCompletion<String>;
304
305impl HostCompletion {
306    pub fn new() -> HostCompletion {
307        let mut hosts = HashSet::new();
308
309        let host_entries = parse_hostfile().unwrap();
310        for host_entry in host_entries {
311            for name in host_entry.names {
312                hosts.insert(name);
313            }
314        }
315
316        let mut elements = Vec::new();
317        for host in hosts {
318            elements.push(host.to_string());
319        }
320
321        HostCompletion { elements, idx: 0 }
322    }
323}
324
325pub struct ServiceCompletion {
326    _inner: VecCompletion<String>,
327}
328
329impl ServiceCompletion {
330    pub fn new() -> ServiceCompletion {
331        let mut services = HashSet::new();
332
333        let service_entries = parse_servicefile(true).unwrap();
334        for service_entry in service_entries {
335            services.insert(service_entry.name);
336            for alias in service_entry.aliases {
337                services.insert(alias);
338            }
339        }
340
341        let mut elements = Vec::new();
342        for service in services {
343            elements.push(service.to_string());
344        }
345
346        ServiceCompletion {
347            _inner: VecCompletion::<String> { elements, idx: 0 },
348        }
349    }
350}
351
352impl Iterator for ServiceCompletion {
353    type Item = String;
354
355    fn next(&mut self) -> Option<String> {
356        self._inner.next()
357    }
358}
359
360impl Stringify for User {
361    fn get_string(&self) -> String {
362        self.name().to_str().unwrap().to_string()
363    }
364}
365
366pub type UserCompletion = VecCompletion<User>;
367
368impl UserCompletion {
369    pub fn new() -> UserCompletion {
370        let elements = unsafe { all_users() }.collect();
371        UserCompletion { elements, idx: 0 }
372    }
373}
374
375pub struct WordListCompletion {
376    _inner: VecCompletion<String>,
377}
378
379impl WordListCompletion {
380    pub fn new(wordlist: &str) -> WordListCompletion {
381        let elements = wordlist.split_whitespace().map(|s| s.to_string()).collect();
382        WordListCompletion {
383            _inner: VecCompletion::<String> { elements, idx: 0 },
384        }
385    }
386}
387
388impl Iterator for WordListCompletion {
389    type Item = String;
390
391    fn next(&mut self) -> Option<String> {
392        self._inner.next()
393    }
394}