nushell on your web browser
nushell wasm terminal
at main 3.4 kB view raw
1use crate::{ 2 cmd::glob::{GlobOptions, expand_path}, 3 error::to_shell_err, 4 globals::{get_pwd, get_vfs}, 5}; 6use nu_engine::CallExt; 7use nu_protocol::{ 8 Category, PipelineData, ShellError, Signature, SyntaxShape, Type, Value, 9 engine::{Command, EngineState, Stack}, 10}; 11use std::sync::Arc; 12use vfs::VfsFileType; 13 14#[derive(Clone)] 15pub struct Rm; 16 17impl Command for Rm { 18 fn name(&self) -> &str { 19 "rm" 20 } 21 22 fn signature(&self) -> Signature { 23 Signature::build("rm") 24 .required( 25 "path", 26 SyntaxShape::OneOf(vec![SyntaxShape::Filepath, SyntaxShape::GlobPattern]), 27 "path to file or directory to remove", 28 ) 29 .switch( 30 "recursive", 31 "remove directories and their contents recursively", 32 Some('r'), 33 ) 34 .input_output_type(Type::Nothing, Type::Nothing) 35 .category(Category::FileSystem) 36 } 37 38 fn description(&self) -> &str { 39 "remove a file or directory from the virtual filesystem." 40 } 41 42 fn run( 43 &self, 44 engine_state: &EngineState, 45 stack: &mut Stack, 46 call: &nu_protocol::engine::Call, 47 _input: PipelineData, 48 ) -> Result<PipelineData, ShellError> { 49 let path_value: Value = call.req(engine_state, stack, 0)?; 50 let recursive = call.has_flag(engine_state, stack, "recursive")?; 51 52 let path_str = match path_value { 53 Value::String { val, .. } | Value::Glob { val, .. } => val, 54 _ => { 55 return Err(ShellError::GenericError { 56 error: "invalid path".into(), 57 msg: "path must be a string or glob pattern".into(), 58 span: Some(call.head), 59 help: None, 60 inner: vec![], 61 }); 62 } 63 }; 64 65 // Prevent removing root 66 if path_str == "/" { 67 return Err(ShellError::GenericError { 68 error: "cannot remove root".to_string(), 69 msg: "refusing to remove root directory".to_string(), 70 span: Some(call.head), 71 help: None, 72 inner: vec![], 73 }); 74 } 75 76 // Expand path (glob or single) into list of paths 77 let is_absolute = path_str.starts_with('/'); 78 let base_path: Arc<vfs::VfsPath> = if is_absolute { get_vfs() } else { get_pwd() }; 79 80 let options = GlobOptions { 81 max_depth: None, 82 no_dirs: false, 83 no_files: false, 84 }; 85 86 let matches = expand_path(&path_str, base_path.clone(), options)?; 87 88 // Remove all matching paths 89 for rel_path in matches { 90 let target = base_path.join(&rel_path).map_err(to_shell_err(call.head))?; 91 let meta = target.metadata().map_err(to_shell_err(call.head))?; 92 match meta.file_type { 93 VfsFileType::File => { 94 target.remove_file().map_err(to_shell_err(call.head))?; 95 } 96 VfsFileType::Directory => { 97 (if recursive { 98 target.remove_dir_all() 99 } else { 100 target.remove_dir() 101 }) 102 .map_err(to_shell_err(call.head))?; 103 } 104 } 105 } 106 107 Ok(PipelineData::Empty) 108 } 109}