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