makepad_shell/
shell.rs

1use std::{
2    path::{Path, PathBuf},
3    fs::File,
4    fs,
5    io::prelude::*,
6    io::BufReader,
7    process::{Command, Stdio, Child}
8};
9
10pub fn shell(cwd: &Path, cmd: &str, args: &[&str]) -> Result<(), String> {
11    let mut cmd_build = Command::new(cmd);
12    
13    cmd_build.args(args)
14        .current_dir(cwd);
15    
16    let mut child = cmd_build.spawn().map_err( | e | format!("Error starting {} in dir {:?} - {:?}", cmd, cwd, e)) ?;
17    
18    let r = child.wait().map_err( | e | format!("Process {} in dir {:?} returned error {:?} ", cmd, cwd, e)) ?;
19    if !r.success() {
20        return Err(format!("Process {} in dir {:?} returned error exit code {:?}", cmd, cwd, r.code()));
21    }
22    Ok(())
23} 
24
25pub fn shell_env(env: &[(&str, &str)], cwd: &Path, cmd: &str, args: &[&str]) -> Result<(), String> {
26    let mut cmd_build = Command::new(cmd);
27    
28    cmd_build.args(args)
29        .current_dir(cwd);
30        
31    for (key, value) in env {
32        cmd_build.env(key, value);
33    }
34    let mut child = cmd_build.spawn().map_err( | e | format!("Error starting {} in dir {:?} - {:?}", cmd, cwd, e)) ?;
35    
36    let r = child.wait().map_err( | e | format!("Process {} in dir {:?} returned error {:?} ", cmd, cwd, e)) ?;
37    if !r.success() {
38        return Err(format!("Process {} in dir {:?} returned error exit code {:?}", cmd, cwd, r.code()));
39    }
40    Ok(())
41}
42
43pub fn shell_env_cap(env: &[(&str, &str)], cwd: &Path, cmd: &str, args: &[&str]) -> Result<String, String> {
44    let mut cmd_build = Command::new(cmd);
45    
46    cmd_build.args(args)
47        .stdout(Stdio::piped())
48        .stderr(Stdio::piped())
49        .current_dir(cwd);
50        
51    for (key, value) in env {
52        cmd_build.env(key, value);
53    }
54    let child = cmd_build.spawn().map_err( | e | format!("Error starting {} in dir {:?} - {:?}", cmd, cwd, e)) ?;
55    let r = child.wait_with_output().map_err( | e | {
56        format!("Process {} in dir {:?} returned error {:?} ", cmd, cwd, e)
57    }) ?;
58    let stderr = std::str::from_utf8(&r.stderr).unwrap_or("could not decode utf8");
59    let stdout = std::str::from_utf8(&r.stdout).unwrap_or("could not decode utf8");
60    if !r.status.success() {
61        return Err(format!("Process {} in dir {:?} returned error exit code {}\n{}\n{}", cmd, cwd, r.status, stderr, stdout));
62    }
63    Ok(format!("{}{}", stdout, stderr))
64}
65
66pub struct ShellChild{
67    child: Child,
68    cwd: PathBuf,
69    cmd: String,
70}
71
72pub fn shell_child_create(env: &[(&str, &str)], cwd: &Path, cmd: &str, args: &[&str]) -> Result<ShellChild, String> {
73    let mut cmd_build = Command::new(cmd);
74        
75    cmd_build.args(args)
76    .stdout(Stdio::piped())
77    .stderr(Stdio::piped())
78    .current_dir(cwd);
79            
80    for (key, value) in env {
81        cmd_build.env(key, value);
82    }
83    let child = cmd_build.spawn().map_err( | e | format!("Error starting {} in dir {:?} - {:?}", cmd, cwd, e)) ?;
84    Ok(ShellChild{
85        child,
86        cwd: cwd.into(),
87        cmd: cmd.into()
88    })
89}
90
91
92pub fn shell_child_wait(child:ShellChild)-> Result<String, String> {
93    let r = child.child.wait_with_output().map_err( | e | {
94        format!("Process {} in dir {:?} returned error {:?} ", child.cmd, child.cwd, e)
95    }) ?;
96    let stderr = std::str::from_utf8(&r.stderr).unwrap_or("could not decode utf8");
97    let stdout = std::str::from_utf8(&r.stdout).unwrap_or("could not decode utf8");
98    if !r.status.success() {
99        return Err(format!("Process {} in dir {:?} returned error exit code {}\n{}\n{}", child.cmd, child.cwd, r.status, stderr, stdout));
100    }
101    Ok(format!("{}{}", stdout, stderr))
102}
103
104pub fn shell_env_cap_split(env: &[(&str, &str)], cwd: &Path, cmd: &str, args: &[&str]) -> (String, String, bool) {
105    let mut cmd_build = Command::new(cmd);
106        
107    cmd_build.args(args)
108    .stdout(Stdio::piped())
109    .stderr(Stdio::piped())
110    .current_dir(cwd);
111            
112    for (key, value) in env {
113        cmd_build.env(key, value);
114    }
115    let child = cmd_build.spawn(); 
116    if let Err(e) = child{
117        return ("".to_string(),format!("Cannot start process {}", e), false);
118    }
119    let r = child.unwrap().wait_with_output();
120    if let Err(e) = r{
121        return ("".to_string(),format!("Wait with output failed for process {}", e), false);
122    }
123    let r = r.unwrap();
124    let stderr = std::str::from_utf8(&r.stderr).unwrap_or("could not decode utf8").to_string();
125    let stdout = std::str::from_utf8(&r.stdout).unwrap_or("could not decode utf8").to_string();
126    (stdout, stderr, r.status.success())
127}
128
129pub fn shell_env_filter(start:&str, minus:Vec<String>, env: &[(&str, &str)], cwd: &Path, cmd: &str,  args: &[&str]) -> Result<(), String> {
130
131    let mut cmd_build = Command::new(cmd);
132    
133    cmd_build.args(args)
134        .stdin(Stdio::piped())
135        .stdout(Stdio::piped())
136        .current_dir(cwd);
137    
138    for (key, value) in env {
139        cmd_build.env(key, value);
140    }
141    
142    let mut child = cmd_build.spawn().map_err( | e | format!("Error starting {} in dir {:?} - {:?}", cmd, cwd, e)) ?;
143    
144    let stdout = child.stdout.take().expect("stdout cannot be taken!");
145    let start = start.to_string();
146    let _stdout_thread = {
147        std::thread::spawn(move || {
148            let mut reader = BufReader::new(stdout);
149            let mut output = false;
150            'a: loop{
151                let mut line = String::new();
152                if let Ok(_) = reader.read_line(&mut line){
153                    if line.contains(&start){
154                        output = true;
155                    }
156                    if output{
157                        for min in &minus{
158                            if line.contains(min){
159                                continue 'a;
160                            }
161                        }
162                        println!("{}",line);
163                    }
164                }
165            }
166        })
167    };
168
169    let r = child.wait().map_err( | e | format!("Process {} in dir {:?} returned error {:?} ", cmd, cwd, e)) ?;
170    if !r.success() {
171        return Err(format!("Process {} in dir {:?} returned error exit code {} ", cmd, cwd, r));
172    }
173    Ok(())
174}
175
176pub fn write_text(path: &Path, data:&str) -> Result<(), String> {
177    mkdir(path.parent().unwrap()) ?;
178    match fs::File::create(path) { 
179        Err(e) => {
180            Err(format!("file create {:?} failed {:?}", path, e))
181        },
182        Ok(mut f) =>{
183            f.write_all(data.as_bytes())
184                .map_err( | _e | format!("Cant write file {:?}", path))
185        }
186    }
187}
188
189pub fn rmdir(path: &Path) -> Result<(), String> {
190    match fs::remove_dir_all(path) {
191        Err(e) => {
192            Err(format!("rmdir {:?} failed {:?}", path, e))
193        },
194        Ok(()) => Ok(())
195    }
196}
197
198
199pub fn mkdir(path: &Path) -> Result<(), String> {
200    match fs::create_dir_all(path) { 
201        Err(e) => {
202            Err(format!("mkdir {:?} failed {:?}", path, e))
203        },
204        Ok(()) => Ok(())
205    }
206}
207
208pub fn rm(path: &Path) -> Result<(), String> {
209    match fs::remove_file(path) {
210        Err(e) => {
211            Err(format!("remove_file {:?} failed {:?}", path, e))
212        },
213        Ok(()) => Ok(())
214    }
215}
216
217
218#[allow(unused)]
219pub fn cp(source_path: &Path, dest_path: &Path, exec: bool) -> Result<(), String> {
220    let data = fs::read(source_path)
221        .map_err( | _e | format!("Cant open input file {:?}", source_path)) ?;
222    mkdir(dest_path.parent().unwrap()) ?;
223    let mut output = File::create(dest_path)
224        .map_err( | _e | format!("Cant open output file {:?}", dest_path)) ?;
225    output.write(&data)
226        .map_err( | _e | format!("Cant write output file {:?}", dest_path)) ?;
227    #[cfg(any(target_os = "macos", target_os = "linux"))]
228    if exec {
229        use std::os::unix::fs::PermissionsExt;
230        std::fs::set_permissions(dest_path, PermissionsExt::from_mode(0o744))
231            .map_err( | _e | format!("Cant set exec permissions on output file {:?}", dest_path)) ?;
232    }
233    Ok(())
234}
235
236pub fn cp_all(source_dir: &Path, dest_dir: &Path, exec: bool) -> Result<(), String> {
237    cp_all_recursive(source_dir, dest_dir, exec)?;
238    Ok(())
239}
240
241fn cp_all_recursive(source_dir: &Path, dest_dir: &Path, exec: bool) -> Result<(), String> {
242    if !source_dir.is_dir() {
243        return Err(format!("{:?} is not a directory", source_dir));
244    }
245
246    mkdir(dest_dir) ?;
247
248    for entry in fs::read_dir(source_dir).map_err(|_e| format!("Unable to read source directory {:?}", source_dir))? {
249        let entry = entry.map_err(|_e| format!("Unable to process directory entry"))?;
250        let source_path = entry.path();
251        if source_path.is_file() {
252            let dest_path = dest_dir.join(source_path.file_name()
253                .ok_or_else(|| format!("Unable to get filename for {:?}", source_path))?);
254
255            cp(&source_path, &dest_path, exec)?;
256        } else if source_path.is_dir() {
257            let dest_path = dest_dir.join(source_path.file_name()
258                    .ok_or_else(|| format!("Unable to get folder name for {:?}", source_path))?);
259
260            cp_all_recursive(&source_path, &dest_path, exec)?;
261        }
262    }
263
264    Ok(())
265}
266
267
268pub fn walk_all(source_dir: &Path, dest_dir: &Path, cb:&mut dyn Fn(&Path, &Path)-> Result<(), String>) -> Result<(), String> {
269    walk_all_recursive(source_dir, dest_dir, cb)?;
270    Ok(())
271}
272
273fn walk_all_recursive(source_dir: &Path, dest_dir: &Path, cb:&mut dyn Fn(&Path, &Path)-> Result<(), String>) -> Result<(), String> {
274    if !source_dir.is_dir() {
275        return Err(format!("{:?} is not a directory", source_dir));
276    }
277    
278    mkdir(dest_dir) ?;
279    
280    for entry in fs::read_dir(source_dir).map_err(|_e| format!("Unable to read source directory {:?}", source_dir))? {
281        let entry = entry.map_err(|_e| format!("Unable to process directory entry"))?;
282        let source_path = entry.path();
283        if source_path.is_file() {
284            cb(&source_path, dest_dir)?;
285            /*let source_file_name = source_path.file_name().ok_or_else(|| format!("Unable to get filename for {:?}", source_path))?.to_string_lossy().to_string();
286            let source_path2 = if let Some(tgt) = rename.get(&source_file_name){
287                //println!("RENAMING {} {}", source_file_name, tgt);
288                source_path.parent().unwrap().join(tgt)
289            }
290            else{
291                source_path
292            };
293            let dest_path = dest_dir.join(source_file_name);
294            cp(&source_path2, &dest_path, exec)?;*/
295        } else if source_path.is_dir() {
296            let dest_path = dest_dir.join(source_path.file_name()
297            .ok_or_else(|| format!("Unable to get folder name for {:?}", source_path))?);
298            
299            walk_all_recursive(&source_path, &dest_path, cb)?;
300        }
301    }
302    
303    Ok(())
304}
305
306pub fn ls(dir: &Path) -> Result<Vec<PathBuf>, String> {
307    let mut result = Vec::new();
308    ls_recursive(dir, dir, &mut result)?;
309    Ok(result)
310}
311
312fn ls_recursive(dir: &Path, prefix: &Path, result: &mut Vec<PathBuf>) -> Result<(), String> {
313    for entry in fs::read_dir(dir).map_err(|_e| format!("Unable to read source directory {:?}", dir))? {
314        let entry = entry.map_err(|_e| format!("Unable to process directory entry"))?;
315        let source_path = entry.path();
316        if source_path.is_file() {
317            let file_name = source_path
318                .file_name()
319                .ok_or_else(|| format!("Unable to get filename"))?
320                .to_str()
321                .ok_or_else(|| format!( "Unable to convert filename to str"))?;
322
323            if !file_name.starts_with('.') {
324                let result_path = source_path.strip_prefix(prefix)
325                    .map_err(|_e| format!("Unable to strip prefix"))?;
326                result.push(result_path.to_path_buf());
327            }
328        } else if source_path.is_dir() {
329            ls_recursive(&source_path, prefix, result)?;
330        }
331    }
332
333    Ok(())
334}