1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
extern crate std;
use std::{io,fs};
use std::io::{Read, Write, BufReader, BufRead};
use std::fs::{File, OpenOptions};
use std::path::PathBuf;
use std::process::{Command, Stdio};
use std::str::FromStr;
use std::env;
fn pick_from_list_external<T: AsRef<str>>(cmd: &mut Command, items: &[T]) -> io::Result<String> {
let process = try!(cmd.stdin(Stdio::piped()).stdout(Stdio::piped()).spawn());
{
let mut process_in = process.stdin.unwrap();
for item in items {
try!(process_in.write_all((item.as_ref().replace("\n", "") + "\n").as_bytes()))
}
}
let mut result = String::new();
try!(process.stdout.unwrap().read_to_string(&mut result));
Ok(result.replace("\n", ""))
}
fn read_parse<T>(tty: &mut File, prompt: &str, min: T, max: T) -> io::Result<T> where T: FromStr + Ord {
try!(tty.write_all(prompt.as_bytes()));
let mut reader = BufReader::new(tty);
let mut result = String::new();
try!(reader.read_line(&mut result));
match result.replace("\n", "").parse::<T>() {
Ok(x) => if x >= min && x <= max { Ok(x) } else { read_parse(reader.into_inner(), prompt, min, max) },
_ => read_parse(reader.into_inner(), prompt, min, max)
}
}
fn pick_from_list_internal<T: AsRef<str>>(items: &[T], prompt: &str) -> io::Result<String> {
let mut tty = try!(OpenOptions::new().read(true).write(true).open("/dev/tty"));
let pad_len = ((items.len() as f32).log10().floor() + 1.0) as usize;
for (i, item) in items.iter().enumerate() {
try!(tty.write_all(format!("{1:0$}. {2}\n", pad_len, i + 1, item.as_ref().replace("\n", "")).as_bytes()))
}
let idx = try!(read_parse::<usize>(&mut tty, prompt, 1, items.len())) - 1;
Ok(items[idx].as_ref().to_string())
}
pub fn pick_from_list<T: AsRef<str>>(cmd: Option<&mut Command>, items: &[T], prompt: &str) -> io::Result<String> {
match cmd {
Some(command) => pick_from_list_external(command, items),
None => pick_from_list_internal(items, prompt),
}
}
pub fn pick_file<C>(cmd: C, path: PathBuf) -> io::Result<PathBuf> where C: Fn() -> Option<Command> {
let mut curpath = path;
loop {
let mut items = try!(fs::read_dir(curpath.clone())).map(|e| {
e.ok().and_then(|ee| ee.file_name().to_str().map(|eee| eee.to_string())).unwrap_or("***PATH ENCODING ERROR***".to_string())
}).collect::<Vec<_>>();
items.insert(0, "..".to_string());
items.insert(0, ".".to_string());
let pick = try!(pick_from_list(cmd().as_mut(), &items[..], curpath.to_str().unwrap_or("***PATH ENCODING ERROR***")));
let newpath = try!(curpath.join(pick).canonicalize());
if let Ok(metadata) = newpath.metadata() {
if metadata.is_dir() {
curpath = newpath;
} else {
return Ok(newpath);
}
}
}
}
pub fn default_menu_cmd() -> Option<Command> {
env::var_os("MENU").map(|s| Command::new(s))
}