1use std::process::Command;
7
8struct MiniShell;
9
10impl pomprt::Editor for MiniShell {
11 fn complete(&self, buffer: &str, cursor: usize) -> Option<pomprt::Completion> {
12 let mut start = buffer[..cursor].rfind(' ').map_or(0, |c| c + 1);
13 let end = buffer[cursor..]
14 .find(' ')
15 .map_or(buffer.len(), |c| cursor + c);
16 let word = &buffer[start..end];
17
18 let results = match buffer[..start].find(|c| c != ' ') {
19 Some(_) => match word.rsplit_once('/') {
20 Some((dir, name)) => {
21 start += dir.len() + 1;
22 complete_file(if dir.is_empty() { "/" } else { dir }, name)
23 }
24 None => complete_file(".", word),
25 },
26 None => complete_file("/usr/bin", word),
27 };
28
29 Some(pomprt::Completion(start..end, results))
30 }
31}
32
33fn complete_file(dir: &str, prefix: &str) -> Vec<String> {
34 let mut entries = std::fs::read_dir(dir).map_or(Vec::new(), |dir| {
35 dir.filter_map(|entry| {
36 let entry = entry.ok()?;
37 let name = entry.file_name().into_string().ok()?;
38 let suffix = if entry.path().is_dir() { "/" } else { " " };
39 name.starts_with(prefix).then(|| name + suffix)
40 })
41 .collect()
42 });
43 entries.sort_unstable();
44 entries
45}
46
47fn main() -> Result<(), Box<dyn std::error::Error>> {
48 let mut sh = pomprt::with(MiniShell, "% ");
49
50 loop {
51 let ok = match sh.read() {
52 Ok(input) => {
53 let mut args = input.split_ascii_whitespace();
54 if let Some(cmd) = args.next() {
55 Command::new(cmd).args(args).spawn()?.wait()?.success()
56 } else {
57 true
58 }
59 }
60 Err(pomprt::Interrupt) => false,
61 Err(pomprt::Eof) => return Ok(()),
62 Err(e) => Err(e)?,
63 };
64
65 sh.set_prompt(if ok { "% " } else { "! " });
66 }
67}