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 }
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}