xref: /NovaShell/src/shell/command/mod.rs (revision 3057dc457df7d7eff10d6e6d5903204e024c9d72)
1 use help::Help;
2 use path_clean::PathClean;
3 use regex::{Captures, Regex};
4 use std::intrinsics::unlikely;
5 use std::io::Read;
6 use std::os::unix::ffi::OsStrExt;
7 use std::{
8     format,
9     fs::{self, File, OpenOptions},
10     io::Write,
11     path::Path,
12     print, println,
13     string::String,
14     vec::Vec,
15 };
16 
17 use crate::env::{Env, ENV_FILE_PATH, ROOT_PATH};
18 use crate::shell::Shell;
19 
20 mod help;
21 
22 #[derive(Debug, PartialEq, Eq, Clone)]
23 enum CommandType {
24     InternalCommand(BuildInCmd),
25     ExternalCommand(String),
26 }
27 
28 #[derive(Debug, PartialEq, Eq, Clone)]
29 pub struct Command {
30     args: Vec<String>,
31     cmd_type: CommandType,
32 }
33 
34 #[derive(Debug, PartialEq, Eq, Clone)]
35 pub enum CommandError {
36     CommandNotFound(String),
37     InvalidArgument(String),
38     WrongArgumentCount(usize),
39     EnvironmentVariableNotFound(String),
40     PathNotFound(String),
41     FileNotFound(String),
42     DirectoryNotFound(String),
43     NotDirectory(String),
44     NotFile(String),
45 }
46 
47 impl CommandError {
48     pub fn handle(e: CommandError) {
49         match e {
50             CommandError::CommandNotFound(command) => {
51                 println!("cannot find command: {}", command)
52             }
53             CommandError::InvalidArgument(argument) => {
54                 println!("invalid argument: {}", argument)
55             }
56             CommandError::WrongArgumentCount(count) => {
57                 println!("argument count incorrect: {}", count)
58             }
59             CommandError::EnvironmentVariableNotFound(env) => {
60                 println!("environment variable not found: {}", env);
61             }
62             CommandError::PathNotFound(path) => {
63                 println!("cannot found file or dirctory: {}", path)
64             }
65             CommandError::FileNotFound(path) => {
66                 println!("cannot found file: {}", path)
67             }
68             CommandError::DirectoryNotFound(path) => {
69                 println!("cannot found dirctory: {}", path)
70             }
71             CommandError::NotDirectory(path) => {
72                 println!("path is not a dirctory: {}", path)
73             }
74             CommandError::NotFile(path) => {
75                 println!("path is not a file: {}", path)
76             }
77         };
78     }
79 }
80 
81 impl Command {
82     fn new(name: String, args: Vec<String>) -> Result<Command, CommandError> {
83         for BuildInCmd(cmd) in BuildInCmd::BUILD_IN_CMD {
84             if name == *cmd {
85                 return Ok(Command {
86                     args,
87                     cmd_type: CommandType::InternalCommand(BuildInCmd(cmd)),
88                 });
89             }
90         }
91 
92         return Ok(Command {
93             args,
94             cmd_type: CommandType::ExternalCommand(name),
95         });
96     }
97 
98     fn from_string(str: String) -> Result<Command, CommandError> {
99         let regex: Regex = Regex::new(r#"'.*'|".*"|[^\s]+"#).unwrap();
100         let hay = str.clone();
101         let mut iter = regex.captures_iter(hay.as_str()).map(|c| {
102             let str = c.get(0).unwrap().as_str();
103             if str.starts_with(|char| char == '\'' || char == '\"')
104                 && str.ends_with(|char| char == '\'' || char == '\"')
105             {
106                 return str[1..str.len() - 1].to_string();
107             } else {
108                 return str.to_string();
109             }
110         });
111         let name = iter.next().unwrap();
112         let re: Regex = Regex::new(r"\$[\w_]+").unwrap();
113         let replacement = |caps: &Captures| -> String {
114             match Env::get(&String::from(&caps[0][1..])) {
115                 Some(value) => value,
116                 None => String::from(&caps[0]),
117             }
118         };
119         let mut args: Vec<String> = Vec::new();
120         for arg in iter.collect::<Vec<String>>().iter() {
121             let arg = re.replace_all(arg.as_str(), &replacement).to_string();
122             match re.captures(arg.as_str()) {
123                 Some(caps) => {
124                     return Err(CommandError::EnvironmentVariableNotFound(String::from(
125                         caps.get(0).unwrap().as_str(),
126                     )))
127                 }
128                 None => args.push(arg),
129             }
130         }
131         let cmd = Command::new(name, args);
132         return cmd;
133     }
134 
135     pub fn from_strings(str: String) -> Vec<Command> {
136         str.split(';')
137             .filter_map(|s| match Command::from_string(String::from(s)) {
138                 Ok(s) => Some(s),
139                 Err(e) => {
140                     CommandError::handle(e);
141                     None
142                 }
143             })
144             .collect::<Vec<Command>>()
145     }
146 }
147 
148 #[derive(Debug, PartialEq, Eq, Clone)]
149 pub struct BuildInCmd(pub &'static str);
150 
151 impl BuildInCmd {
152     pub const BUILD_IN_CMD: &[BuildInCmd] = &[
153         BuildInCmd("cd"),
154         BuildInCmd("ls"),
155         BuildInCmd("cat"),
156         BuildInCmd("touch"),
157         BuildInCmd("mkdir"),
158         BuildInCmd("rm"),
159         BuildInCmd("rmdir"),
160         BuildInCmd("pwd"),
161         BuildInCmd("cp"),
162         BuildInCmd("exec"),
163         BuildInCmd("echo"),
164         BuildInCmd("reboot"),
165         BuildInCmd("free"),
166         BuildInCmd("kill"),
167         BuildInCmd("help"),
168         BuildInCmd("export"),
169         BuildInCmd("env"),
170         BuildInCmd("compgen"),
171         BuildInCmd("complete"),
172     ];
173 }
174 
175 impl Shell {
176     pub fn exec_internal_command(
177         &mut self,
178         cmd: &str,
179         args: &Vec<String>,
180     ) -> Result<(), CommandError> {
181         match cmd {
182             "cd" => self.shell_cmd_cd(args),
183             "ls" => self.shell_cmd_ls(args),
184             "cat" => self.shell_cmd_cat(args),
185             "touch" => self.shell_cmd_touch(args),
186             "mkdir" => self.shell_cmd_mkdir(args),
187             "rm" => self.shell_cmd_rm(args),
188             "rmdir" => self.shell_cmd_rmdir(args),
189             "pwd" => self.shell_cmd_pwd(args),
190             "cp" => self.shell_cmd_cp(args),
191             "exec" => self.shell_cmd_exec(args),
192             "echo" => self.shell_cmd_echo(args),
193             "reboot" => self.shell_cmd_reboot(args),
194             "free" => self.shell_cmd_free(args),
195             "kill" => self.shell_cmd_kill(args),
196             "help" => self.shell_cmd_help(args),
197             "export" => self.shell_cmd_export(args),
198             "env" => self.shell_cmd_env(args),
199             "compgen" => self.shell_cmd_compgen(args),
200             "complete" => self.shell_cmd_complete(args),
201 
202             _ => Err(CommandError::CommandNotFound(String::from(cmd))),
203         }
204     }
205 
206     pub fn exec_external_command(&mut self, path: String, args: &Vec<String>) {
207         let mut full_args = args.clone();
208         full_args.insert(0, path.clone());
209         self.shell_cmd_exec(&full_args).unwrap_or_else(|e| {
210             let err = match e {
211                 CommandError::FileNotFound(rp) => CommandError::CommandNotFound(rp),
212                 _ => e,
213             };
214             CommandError::handle(err);
215         })
216     }
217 
218     pub fn exec_command(&mut self, command: &Command) {
219         match &command.cmd_type {
220             CommandType::ExternalCommand(path) => {
221                 self.exec_external_command(path.to_string(), &command.args);
222             }
223 
224             CommandType::InternalCommand(BuildInCmd(cmd)) => {
225                 match self.exec_internal_command(cmd, &command.args) {
226                     Ok(_) => {}
227                     Err(e) => CommandError::handle(e),
228                 }
229                 if command.args.contains(&String::from("--help")) {
230                     Help::shell_help(cmd);
231                 }
232             }
233         }
234     }
235 
236     fn shell_cmd_cd(&mut self, args: &Vec<String>) -> Result<(), CommandError> {
237         let path = match args.len() {
238             0 => String::from(ROOT_PATH),
239             1 => self.is_dir(args.get(0).unwrap())?,
240             _ => return Err(CommandError::WrongArgumentCount(args.len())),
241         };
242         self.chdir(&path);
243         Ok(())
244     }
245 
246     fn shell_cmd_ls(&self, args: &Vec<String>) -> Result<(), CommandError> {
247         let path = match args.len() {
248             0 => Self::current_dir(),
249             1 => self.is_dir(args.get(0).unwrap())?,
250             _ => return Err(CommandError::WrongArgumentCount(args.len())),
251         };
252         let dir = match fs::read_dir(Path::new(&path)) {
253             Ok(readdir) => readdir,
254             Err(_) => return Err(CommandError::InvalidArgument(path)),
255         };
256 
257         for entry in dir {
258             let entry = entry.unwrap();
259             if entry.file_type().unwrap().is_dir() {
260                 crate::shell::Printer::print_color(
261                     entry.file_name().as_bytes(),
262                     0x000088ff,
263                     0x00000000,
264                 );
265                 print!("    ");
266             } else {
267                 print!("{}    ", entry.file_name().into_string().unwrap());
268             }
269         }
270         println!();
271         Ok(())
272     }
273 
274     fn shell_cmd_cat(&self, args: &Vec<String>) -> Result<(), CommandError> {
275         if args.len() <= 0 {
276             return Err(CommandError::WrongArgumentCount(args.len()));
277         }
278         let path = self.is_file(args.get(0).unwrap())?;
279         let mut buf: Vec<u8> = Vec::new();
280 
281         File::open(path).unwrap().read_to_end(&mut buf).unwrap();
282         if args.len() == 1 {
283             println!("{}", String::from_utf8(buf.clone()).unwrap());
284         }
285 
286         //TODO: 这部分应该放在`Shell`中,所有指令公用
287         if args.len() == 3 {
288             let mut target_path = args.get(2).unwrap().clone();
289             match self.is_file(&target_path) {
290                 Ok(str) => target_path = str,
291                 Err(e) => return Err(e),
292             }
293 
294             if args[1] == ">" {
295                 match OpenOptions::new().write(true).open(target_path) {
296                     Ok(mut file) => {
297                         file.write_all(&buf).unwrap();
298                     }
299                     Err(e) => print!("{e}"),
300                 }
301             } else if args[1] == ">>" {
302                 match OpenOptions::new().append(true).open(target_path) {
303                     Ok(mut file) => {
304                         file.write_all(&buf).unwrap();
305                     }
306                     Err(e) => print!("{e}"),
307                 }
308             }
309         }
310         Ok(())
311     }
312 
313     fn shell_cmd_touch(&self, args: &Vec<String>) -> Result<(), CommandError> {
314         if unlikely(args.len() != 1) {
315             return Err(CommandError::WrongArgumentCount(args.len()));
316         }
317         let path = args.get(0).unwrap();
318 
319         //路径中提取目录和文件名
320         let index = path.rfind('/').unwrap_or(0);
321         let dir = &path[..index];
322         let file_name = &path[index..];
323 
324         //判断文件所在目录是否存在
325         let str = self.is_dir(&dir.to_string())?;
326         //判断文件是否存在,存在时不操作,不存在时创建文件
327         let abs_path = format!("{}/{}", str, file_name);
328         if !Path::new(&abs_path).exists() {
329             File::create(&abs_path).unwrap();
330         }
331         Ok(())
332     }
333 
334     fn shell_cmd_mkdir(&self, args: &Vec<String>) -> Result<(), CommandError> {
335         if unlikely(args.len() != 1) {
336             return Err(CommandError::WrongArgumentCount(args.len()));
337         }
338         let nowpath = Self::current_dir();
339         let path = args.get(0).unwrap();
340         let opt_path = nowpath + "/" + path;
341         let target_path;
342         if path.starts_with("/") {
343             target_path = path;
344         } else {
345             target_path = &opt_path;
346         }
347         if let Err(e) = fs::create_dir_all(target_path) {
348             print!("{e}")
349         }
350         Ok(())
351     }
352 
353     fn shell_cmd_rm(&self, args: &Vec<String>) -> Result<(), CommandError> {
354         if unlikely(args.len() != 1) {
355             return Err(CommandError::WrongArgumentCount(args.len()));
356         }
357         let path = self.is_file(args.get(0).unwrap())?;
358         let path_cstr = std::ffi::CString::new(path).unwrap();
359         unsafe {
360             libc::syscall(libc::SYS_unlinkat, 0, path_cstr.as_ptr(), 0, 0, 0, 0);
361         }
362         Ok(())
363     }
364 
365     fn shell_cmd_rmdir(&self, args: &Vec<String>) -> Result<(), CommandError> {
366         if unlikely(args.len() != 1) {
367             return Err(CommandError::WrongArgumentCount(args.len()));
368         }
369         let path = self.is_dir(args.get(0).unwrap())?;
370         let path_cstr = std::ffi::CString::new(path).unwrap();
371         unsafe { libc::unlinkat(0, path_cstr.as_ptr(), libc::AT_REMOVEDIR) };
372         Ok(())
373     }
374 
375     fn shell_cmd_pwd(&self, args: &Vec<String>) -> Result<(), CommandError> {
376         if unlikely(args.len() != 0) {
377             return Err(CommandError::WrongArgumentCount(args.len()));
378         }
379         println!("{}", Self::current_dir());
380         Ok(())
381     }
382 
383     fn shell_cmd_cp(&self, args: &Vec<String>) -> Result<(), CommandError> {
384         if args.len() == 2 {
385             let mut src_path = args.get(0).unwrap().clone();
386             let mut target_path = args.get(1).unwrap().clone();
387 
388             match self.is_file(&src_path) {
389                 Ok(str) => src_path = str,
390                 Err(e) => return Err(e),
391             }
392 
393             match self.is_file_or_dir(&target_path) {
394                 Ok(str) => target_path = str,
395                 Err(e) => {
396                     let prefix = &target_path[..target_path.rfind('/').unwrap_or(0)];
397                     if !Path::new(prefix).is_dir() {
398                         return Err(e);
399                     }
400                 }
401             }
402 
403             if Path::new(&src_path).is_dir() {
404                 let name = &src_path[src_path.rfind('/').unwrap_or(0)..];
405                 target_path = format!("{}/{}", target_path, name);
406             }
407 
408             let mut src_file = File::open(&src_path).unwrap();
409             let mut target_file = File::create(target_path).unwrap();
410             let mut buf: Vec<u8> = Vec::new();
411             src_file.read_to_end(&mut buf).unwrap();
412             target_file.write_all(&buf).unwrap();
413             return Ok(());
414         }
415         return Err(CommandError::WrongArgumentCount(args.len()));
416     }
417 
418     pub fn shell_cmd_exec(&self, args: &Vec<String>) -> Result<(), CommandError> {
419         if unlikely(args.len() <= 0) {
420             return Err(CommandError::WrongArgumentCount(args.len()));
421         }
422         let path = args.get(0).unwrap();
423         //在环境变量中搜索
424         //TODO: 放在一个函数里来实现
425         let mut real_path = String::new();
426         if !path.contains('/') {
427             let mut dir_collection = Env::path();
428             dir_collection.insert(0, Self::current_dir());
429             for dir in dir_collection {
430                 let possible_path = format!("{}/{}", dir, path);
431                 if Path::new(&possible_path).is_file() {
432                     real_path = possible_path;
433                     break;
434                 }
435             }
436             if real_path.is_empty() {
437                 return Err(CommandError::FileNotFound(path.clone()));
438             }
439         } else {
440             match self.is_file(path) {
441                 Ok(path) => real_path = path,
442                 Err(e) => return Err(e),
443             }
444         }
445 
446         let mut args = args.clone();
447         // 如果文件不存在,返回错误
448         if !Path::new(&real_path).is_file() {
449             // println!("{}: command not found", real_path);
450             return Err(CommandError::FileNotFound(real_path.clone()));
451         }
452 
453         let pid: libc::pid_t = unsafe {
454             libc::syscall(libc::SYS_fork, 0, 0, 0, 0, 0, 0)
455                 .try_into()
456                 .unwrap()
457         };
458 
459         let name = &real_path[real_path.rfind('/').map(|pos| pos + 1).unwrap_or(0)..];
460         *args.get_mut(0).unwrap() = name.to_string();
461         let mut retval = 0;
462         if pid == 0 {
463             let path_cstr = std::ffi::CString::new(real_path).unwrap();
464             let args_cstr = args
465                 .iter()
466                 .map(|str| std::ffi::CString::new(str.as_str()).unwrap())
467                 .collect::<Vec<std::ffi::CString>>();
468             let mut args_ptr = args_cstr
469                 .iter()
470                 .map(|c_str| c_str.as_ptr())
471                 .collect::<Vec<*const i8>>();
472             args_ptr.push(std::ptr::null());
473             let argv = args_ptr.as_ptr();
474 
475             unsafe {
476                 libc::execv(path_cstr.as_ptr(), argv);
477             }
478         } else {
479             if args.last().unwrap() != &"&" {
480                 unsafe { libc::waitpid(pid, &mut retval as *mut i32, 0) };
481             } else {
482                 println!("[1] {}", pid);
483             }
484         }
485         return Ok(());
486     }
487 
488     fn shell_cmd_echo(&self, args: &Vec<String>) -> Result<(), CommandError> {
489         if args.len() > 0 {
490             let str = args.get(0).unwrap();
491             if args.len() == 1 {
492                 println!("{str}");
493             }
494 
495             //TODO: 和`cat`中的一样,应放在`Shell`中
496             if args.len() == 3 {
497                 let mut target_path = args.get(2).unwrap().clone();
498                 match self.is_file(&target_path) {
499                     Ok(str) => target_path = str,
500                     Err(e) => return Err(e),
501                 }
502                 if args[1] == ">" {
503                     match OpenOptions::new().write(true).open(target_path) {
504                         Ok(mut file) => {
505                             file.write_all(str.as_bytes()).unwrap();
506                         }
507                         Err(e) => print!("{e}"),
508                     }
509                 } else if args[1] == ">>" {
510                     match OpenOptions::new().append(true).open(target_path) {
511                         Ok(mut file) => {
512                             file.write_all(str.as_bytes()).unwrap();
513                         }
514                         Err(e) => print!("{e}"),
515                     }
516                 }
517             }
518             return Ok(());
519         }
520         return Err(CommandError::WrongArgumentCount(args.len()));
521     }
522 
523     fn shell_cmd_reboot(&self, args: &Vec<String>) -> Result<(), CommandError> {
524         if args.len() == 0 {
525             unsafe { libc::syscall(libc::SYS_reboot, 0, 0, 0, 0, 0, 0) };
526             return Ok(());
527         } else {
528             return Err(CommandError::WrongArgumentCount(args.len()));
529         }
530     }
531 
532     fn shell_cmd_free(&self, args: &Vec<String>) -> Result<(), CommandError> {
533         if args.len() == 1 && args.get(0).unwrap() != "-m" {
534             return Err(CommandError::InvalidArgument(
535                 args.get(0).unwrap().to_string(),
536             ));
537         }
538 
539         struct Mstat {
540             total: u64,      // 计算机的总内存数量大小
541             used: u64,       // 已使用的内存大小
542             free: u64,       // 空闲物理页所占的内存大小
543             shared: u64,     // 共享的内存大小
544             cache_used: u64, // 位于slab缓冲区中的已使用的内存大小
545             cache_free: u64, // 位于slab缓冲区中的空闲的内存大小
546             available: u64,  // 系统总空闲内存大小(包括kmalloc缓冲区)
547         }
548 
549         let mut mst = Mstat {
550             total: 0,
551             used: 0,
552             free: 0,
553             shared: 0,
554             cache_used: 0,
555             cache_free: 0,
556             available: 0,
557         };
558 
559         let mut info_file = File::open("/proc/meminfo").unwrap();
560         let mut buf: Vec<u8> = Vec::new();
561         info_file.read_to_end(&mut buf).unwrap();
562         let str = String::from_utf8(buf).unwrap();
563         let info = str
564             .split(&['\n', '\t', ' '])
565             .filter_map(|str| str.parse::<u64>().ok())
566             .collect::<Vec<u64>>();
567         mst.total = *info.get(0).unwrap();
568         mst.free = *info.get(1).unwrap();
569         mst.used = mst.total - mst.free;
570 
571         print!("\ttotal\t\tused\t\tfree\t\tshared\t\tcache_used\tcache_free\tavailable\n");
572         print!("Mem:\t");
573 
574         if args.len() == 0 {
575             print!(
576                 "{}\t\t{}\t\t{}\t\t{}\t\t{}\t\t{}\t\t{}\n",
577                 mst.total,
578                 mst.used,
579                 mst.free,
580                 mst.shared,
581                 mst.cache_used,
582                 mst.cache_free,
583                 mst.available
584             );
585         } else {
586             print!(
587                 "{}\t\t{}\t\t{}\t\t{}\t\t{}\t\t{}\n",
588                 mst.total >> 10,
589                 mst.used >> 10,
590                 mst.free >> 10,
591                 mst.shared >> 10,
592                 mst.cache_used >> 10,
593                 mst.available >> 10
594             );
595         }
596         Ok(())
597     }
598 
599     fn shell_cmd_kill(&self, args: &Vec<String>) -> Result<(), CommandError> {
600         if unlikely(args.len() != 1) {
601             return Err(CommandError::WrongArgumentCount(args.len()));
602         }
603 
604         let pid = match args.get(0).unwrap().parse::<i32>() {
605             Ok(x) => x,
606             Err(_) => {
607                 return Err(CommandError::InvalidArgument(
608                     args.get(0).unwrap().to_string(),
609                 ))
610             }
611         };
612         unsafe {
613             libc::kill(pid, libc::SIGTERM);
614         }
615         Ok(())
616     }
617 
618     fn shell_cmd_help(&self, args: &Vec<String>) -> Result<(), CommandError> {
619         if args.len() == 0 {
620             for BuildInCmd(cmd) in BuildInCmd::BUILD_IN_CMD {
621                 Help::shell_help(cmd)
622             }
623             return Ok(());
624         }
625         return Err(CommandError::WrongArgumentCount(args.len()));
626     }
627 
628     fn shell_cmd_export(&self, args: &Vec<String>) -> Result<(), CommandError> {
629         if args.len() == 1 {
630             let pair = args.get(0).unwrap().split('=').collect::<Vec<&str>>();
631 
632             if pair.len() == 2 && !pair.contains(&"") {
633                 let name = pair.get(0).unwrap().to_string();
634                 let value = pair.get(1).unwrap().to_string();
635                 Env::insert(name, value);
636                 return Ok(());
637             } else {
638                 return Err(CommandError::InvalidArgument(args.get(0).unwrap().clone()));
639             }
640         }
641         return Err(CommandError::WrongArgumentCount(args.len()));
642     }
643 
644     fn shell_cmd_env(&self, args: &Vec<String>) -> Result<(), CommandError> {
645         if args.len() == 0 {
646             let mut file = File::open(ENV_FILE_PATH).unwrap();
647             let mut buf: Vec<u8> = Vec::new();
648             file.read_to_end(&mut buf).unwrap();
649             println!("{}", String::from_utf8(buf).unwrap());
650             return Ok(());
651         } else {
652             return Err(CommandError::InvalidArgument(args.get(0).unwrap().clone()));
653         }
654     }
655 
656     fn shell_cmd_compgen(&self, _args: &Vec<String>) -> Result<(), CommandError> {
657         //TODO
658         Ok(())
659     }
660 
661     fn shell_cmd_complete(&self, _args: &Vec<String>) -> Result<(), CommandError> {
662         //TODO
663         Ok(())
664     }
665 
666     fn path_format(&self, path: &String) -> Result<String, CommandError> {
667         let mut abs_path = path.clone();
668         if !path.starts_with('/') {
669             abs_path = format!("{}/{}", Self::current_dir(), path);
670         }
671         let path = Path::new(&abs_path).clean();
672         let mut fmt_path = path.to_str().unwrap().to_string();
673         let replacement = |_caps: &regex::Captures| -> String { String::from("/") };
674         let re = regex::Regex::new(r"\/{2,}").unwrap();
675         fmt_path = re.replace_all(fmt_path.as_str(), replacement).to_string();
676         return Ok(fmt_path);
677     }
678 
679     fn is_file(&self, path_str: &String) -> Result<String, CommandError> {
680         match self.path_format(path_str) {
681             Ok(path_str) => {
682                 let path = Path::new(&path_str);
683                 if !path.is_file() {
684                     return Err(CommandError::NotFile(path_str.clone()));
685                 };
686                 Ok(path_str)
687             }
688             Err(_) => Err(CommandError::FileNotFound(path_str.clone())),
689         }
690     }
691 
692     fn is_dir(&self, path_str: &String) -> Result<String, CommandError> {
693         match self.path_format(path_str) {
694             Ok(path_str) => {
695                 let path = Path::new(&path_str);
696                 if !path.is_dir() {
697                     return Err(CommandError::NotDirectory(path_str.clone()));
698                 };
699                 Ok(path_str)
700             }
701             Err(_) => Err(CommandError::DirectoryNotFound(path_str.clone())),
702         }
703     }
704 
705     fn is_file_or_dir(&self, path_str: &String) -> Result<String, CommandError> {
706         match self.path_format(path_str) {
707             Ok(path_str) => Ok(path_str),
708             Err(_) => Err(CommandError::PathNotFound(path_str.clone())),
709         }
710     }
711 }
712