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