diff options
Diffstat (limited to 'xtask/src/not_bash.rs')
-rw-r--r-- | xtask/src/not_bash.rs | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/xtask/src/not_bash.rs b/xtask/src/not_bash.rs new file mode 100644 index 000000000..56d6c6c2d --- /dev/null +++ b/xtask/src/not_bash.rs | |||
@@ -0,0 +1,123 @@ | |||
1 | //! A bad shell -- small cross platform module for writing glue code | ||
2 | use std::{ | ||
3 | cell::RefCell, | ||
4 | env, | ||
5 | ffi::OsStr, | ||
6 | fs, | ||
7 | path::PathBuf, | ||
8 | process::{Command, Stdio}, | ||
9 | }; | ||
10 | |||
11 | use anyhow::{bail, Context, Result}; | ||
12 | |||
13 | macro_rules! _run { | ||
14 | ($($expr:expr),*) => { | ||
15 | run!($($expr),*; echo = true) | ||
16 | }; | ||
17 | ($($expr:expr),* ; echo = $echo:expr) => { | ||
18 | $crate::not_bash::run_process(format!($($expr),*), $echo) | ||
19 | }; | ||
20 | } | ||
21 | pub(crate) use _run as run; | ||
22 | |||
23 | pub struct Pushd { | ||
24 | _p: (), | ||
25 | } | ||
26 | |||
27 | pub fn pushd(path: impl Into<PathBuf>) -> Pushd { | ||
28 | Env::with(|env| env.pushd(path.into())); | ||
29 | Pushd { _p: () } | ||
30 | } | ||
31 | |||
32 | impl Drop for Pushd { | ||
33 | fn drop(&mut self) { | ||
34 | Env::with(|env| env.popd()) | ||
35 | } | ||
36 | } | ||
37 | |||
38 | pub fn rm(glob: &str) -> Result<()> { | ||
39 | let cwd = Env::with(|env| env.cwd()); | ||
40 | ls(glob)?.into_iter().try_for_each(|it| fs::remove_file(cwd.join(it)))?; | ||
41 | Ok(()) | ||
42 | } | ||
43 | |||
44 | pub fn ls(glob: &str) -> Result<Vec<PathBuf>> { | ||
45 | let cwd = Env::with(|env| env.cwd()); | ||
46 | let mut res = Vec::new(); | ||
47 | for entry in fs::read_dir(&cwd)? { | ||
48 | let entry = entry?; | ||
49 | if matches(&entry.file_name(), glob) { | ||
50 | let path = entry.path(); | ||
51 | let path = path.strip_prefix(&cwd).unwrap(); | ||
52 | res.push(path.to_path_buf()) | ||
53 | } | ||
54 | } | ||
55 | return Ok(res); | ||
56 | |||
57 | fn matches(file_name: &OsStr, glob: &str) -> bool { | ||
58 | assert!(glob.starts_with('*')); | ||
59 | file_name.to_string_lossy().ends_with(&glob[1..]) | ||
60 | } | ||
61 | } | ||
62 | |||
63 | #[doc(hidden)] | ||
64 | pub fn run_process(cmd: String, echo: bool) -> Result<String> { | ||
65 | run_process_inner(&cmd, echo).with_context(|| format!("process `{}` failed", cmd)) | ||
66 | } | ||
67 | |||
68 | fn run_process_inner(cmd: &str, echo: bool) -> Result<String> { | ||
69 | let cwd = Env::with(|env| env.cwd()); | ||
70 | let mut args = shelx(cmd); | ||
71 | let binary = args.remove(0); | ||
72 | |||
73 | if echo { | ||
74 | println!("> {}", cmd) | ||
75 | } | ||
76 | |||
77 | let output = Command::new(binary) | ||
78 | .args(args) | ||
79 | .current_dir(cwd) | ||
80 | .stdin(Stdio::null()) | ||
81 | .stderr(Stdio::inherit()) | ||
82 | .output()?; | ||
83 | let stdout = String::from_utf8(output.stdout)?; | ||
84 | |||
85 | if echo { | ||
86 | print!("{}", stdout) | ||
87 | } | ||
88 | |||
89 | if !output.status.success() { | ||
90 | bail!("{}", output.status) | ||
91 | } | ||
92 | |||
93 | Ok(stdout) | ||
94 | } | ||
95 | |||
96 | // FIXME: some real shell lexing here | ||
97 | fn shelx(cmd: &str) -> Vec<String> { | ||
98 | cmd.split_whitespace().map(|it| it.to_string()).collect() | ||
99 | } | ||
100 | |||
101 | #[derive(Default)] | ||
102 | struct Env { | ||
103 | pushd_stack: Vec<PathBuf>, | ||
104 | } | ||
105 | |||
106 | impl Env { | ||
107 | fn with<F: FnOnce(&mut Env) -> T, T>(f: F) -> T { | ||
108 | thread_local! { | ||
109 | static ENV: RefCell<Env> = Default::default(); | ||
110 | } | ||
111 | ENV.with(|it| f(&mut *it.borrow_mut())) | ||
112 | } | ||
113 | |||
114 | fn pushd(&mut self, dir: PathBuf) { | ||
115 | self.pushd_stack.push(dir) | ||
116 | } | ||
117 | fn popd(&mut self) { | ||
118 | self.pushd_stack.pop().unwrap(); | ||
119 | } | ||
120 | fn cwd(&self) -> PathBuf { | ||
121 | self.pushd_stack.last().cloned().unwrap_or_else(|| env::current_dir().unwrap()) | ||
122 | } | ||
123 | } | ||