nushell on your web browser
nushell wasm terminal

format

ptr.pet 80408401 5600d1b6

verified
+93 -93
+35 -33
src/cmd/glob.rs
··· 87 87 // Normalize pattern: remove leading / for relative matching 88 88 let normalized_pattern = pattern_str.trim_start_matches('/'); 89 89 let is_recursive = normalized_pattern.contains("**"); 90 - 90 + 91 91 // Collect matching paths 92 92 let mut matches = Vec::new(); 93 - 93 + 94 94 fn walk_directory( 95 95 current_path: Arc<vfs::VfsPath>, 96 96 current_relative_path: String, ··· 111 111 if let Ok(entries) = current_path.read_dir() { 112 112 for entry in entries { 113 113 let filename = entry.filename(); 114 - let entry_path = current_path.join(&filename) 114 + let entry_path = 115 + current_path 116 + .join(&filename) 117 + .map_err(|e| ShellError::GenericError { 118 + error: "path error".into(), 119 + msg: e.to_string(), 120 + span: None, 121 + help: None, 122 + inner: vec![], 123 + })?; 124 + 125 + // Build relative path from base 126 + let new_relative = if current_relative_path.is_empty() { 127 + filename.clone() 128 + } else { 129 + format!("{}/{}", current_relative_path, filename) 130 + }; 131 + 132 + let metadata = entry_path 133 + .metadata() 115 134 .map_err(|e| ShellError::GenericError { 116 135 error: "path error".into(), 117 136 msg: e.to_string(), ··· 119 138 help: None, 120 139 inner: vec![], 121 140 })?; 122 - 123 - // Build relative path from base 124 - let new_relative = if current_relative_path.is_empty() { 125 - filename.clone() 126 - } else { 127 - format!("{}/{}", current_relative_path, filename) 128 - }; 129 - 130 - let metadata = entry_path.metadata().map_err(|e| ShellError::GenericError { 131 - error: "path error".into(), 132 - msg: e.to_string(), 133 - span: None, 134 - help: None, 135 - inner: vec![], 136 - })?; 137 - 141 + 138 142 // Check if this path matches the pattern 139 143 // For patterns without path separators, match just the filename 140 144 // For patterns with path separators, match the full relative path ··· 143 147 } else { 144 148 &filename 145 149 }; 146 - 150 + 147 151 if pattern.matches(path_to_match) { 148 152 let should_include = match metadata.file_type { 149 153 VfsFileType::Directory => !no_dirs, ··· 153 157 matches.push(new_relative.clone()); 154 158 } 155 159 } 156 - 160 + 157 161 // Recursively walk into subdirectories 158 162 if metadata.file_type == VfsFileType::Directory { 159 163 // Continue if: recursive pattern, or we haven't reached max depth, or pattern has more components 160 - let should_recurse = is_recursive 164 + let should_recurse = is_recursive 161 165 || current_depth < max_depth 162 - || (normalized_pattern.contains('/') && current_depth < normalized_pattern.split('/').count()); 163 - 166 + || (normalized_pattern.contains('/') 167 + && current_depth < normalized_pattern.split('/').count()); 168 + 164 169 if should_recurse { 165 170 walk_directory( 166 171 Arc::new(entry_path), ··· 276 281 277 282 // Determine if pattern is absolute (starts with /) 278 283 let is_absolute = pattern_str.starts_with('/'); 279 - let base_path = if is_absolute { 280 - get_vfs() 281 - } else { 282 - get_pwd() 283 - }; 284 + let base_path = if is_absolute { get_vfs() } else { get_pwd() }; 284 285 285 286 // Use the glob_match function 286 287 let options = GlobOptions { ··· 288 289 no_dirs, 289 290 no_files, 290 291 }; 291 - 292 + 292 293 let matches = glob_match(&pattern_str, base_path, options)?; 293 294 294 295 // Convert matches to Value stream 295 296 let signals = engine_state.signals().clone(); 296 - let values = matches.into_iter().map(move |path| Value::string(path, span)); 297 - 297 + let values = matches 298 + .into_iter() 299 + .map(move |path| Value::string(path, span)); 300 + 298 301 Ok(PipelineData::list_stream( 299 302 ListStream::new(values, span, signals.clone()), 300 303 None, 301 304 )) 302 305 } 303 306 } 304 -
+8 -14
src/cmd/ls.rs
··· 1 1 use std::time::{SystemTime, UNIX_EPOCH}; 2 2 3 3 use crate::{ 4 - cmd::glob::{expand_path, GlobOptions}, 4 + cmd::glob::{GlobOptions, expand_path}, 5 5 error::to_shell_err, 6 6 globals::{get_pwd, get_vfs}, 7 7 }; 8 - use std::sync::Arc; 9 8 use jacquard::chrono; 10 9 use nu_engine::CallExt; 11 10 use nu_protocol::{ 12 11 Category, ListStream, PipelineData, Record, ShellError, Signature, SyntaxShape, Type, Value, 13 12 engine::{Command, EngineState, Stack}, 14 13 }; 14 + use std::sync::Arc; 15 15 16 16 #[derive(Clone)] 17 17 pub struct Ls; ··· 78 78 }; 79 79 80 80 let is_absolute = path_str.starts_with('/'); 81 - let base_path: Arc<vfs::VfsPath> = if is_absolute { 82 - get_vfs() 83 - } else { 84 - pwd.clone() 85 - }; 81 + let base_path: Arc<vfs::VfsPath> = if is_absolute { get_vfs() } else { pwd.clone() }; 86 82 87 83 let options = GlobOptions { 88 84 max_depth: None, ··· 145 141 Ok(Some(Value::record(record, span))) 146 142 }; 147 143 148 - let entries = matches 149 - .into_iter() 150 - .flat_map(move |rel_path| { 151 - make_record(&rel_path) 152 - .transpose() 153 - .map(|res| res.unwrap_or_else(|err| Value::error(err, span))) 154 - }); 144 + let entries = matches.into_iter().flat_map(move |rel_path| { 145 + make_record(&rel_path) 146 + .transpose() 147 + .map(|res| res.unwrap_or_else(|err| Value::error(err, span))) 148 + }); 155 149 156 150 let signals = engine_state.signals().clone(); 157 151 Ok(PipelineData::list_stream(
+22 -13
src/cmd/mv.rs
··· 1 1 use std::io::{Read, Write}; 2 2 3 3 use crate::{ 4 - cmd::glob::{expand_path, GlobOptions}, 4 + cmd::glob::{GlobOptions, expand_path}, 5 5 error::to_shell_err, 6 6 globals::{get_pwd, get_vfs}, 7 7 }; 8 - use std::sync::Arc; 9 8 use nu_engine::CallExt; 10 9 use nu_protocol::{ 11 10 Category, PipelineData, ShellError, Signature, SyntaxShape, Type, Value, 12 11 engine::{Command, EngineState, Stack}, 13 12 }; 13 + use std::sync::Arc; 14 14 use vfs::{VfsError, VfsFileType}; 15 15 16 16 #[derive(Clone)] ··· 77 77 78 78 // Expand source path (glob or single) into list of paths 79 79 let is_absolute = source_str.starts_with('/'); 80 - let base_path: Arc<vfs::VfsPath> = if is_absolute { 81 - get_vfs() 82 - } else { 83 - get_pwd() 84 - }; 80 + let base_path: Arc<vfs::VfsPath> = if is_absolute { get_vfs() } else { get_pwd() }; 85 81 86 82 let options = GlobOptions { 87 83 max_depth: None, ··· 90 86 }; 91 87 92 88 let matches = expand_path(&source_str, base_path.clone(), options)?; 93 - let is_glob = matches.len() > 1 || source_str.contains('*') || source_str.contains('?') || source_str.contains('[') || source_str.contains("**"); 89 + let is_glob = matches.len() > 1 90 + || source_str.contains('*') 91 + || source_str.contains('?') 92 + || source_str.contains('[') 93 + || source_str.contains("**"); 94 94 95 95 // Resolve destination 96 96 let dest = get_pwd() ··· 99 99 100 100 // For glob patterns, destination must be a directory 101 101 if is_glob { 102 - let dest_meta = dest.metadata().map_err(to_shell_err(call.arguments_span()))?; 102 + let dest_meta = dest 103 + .metadata() 104 + .map_err(to_shell_err(call.arguments_span()))?; 103 105 if dest_meta.file_type != VfsFileType::Directory { 104 106 return Err(ShellError::GenericError { 105 107 error: "destination must be a directory".to_string(), ··· 113 115 114 116 // Move each matching file/directory 115 117 for rel_path in matches { 116 - let source = base_path.join(&rel_path).map_err(to_shell_err(call.arguments_span()))?; 117 - let source_meta = source.metadata().map_err(to_shell_err(call.arguments_span()))?; 118 + let source = base_path 119 + .join(&rel_path) 120 + .map_err(to_shell_err(call.arguments_span()))?; 121 + let source_meta = source 122 + .metadata() 123 + .map_err(to_shell_err(call.arguments_span()))?; 118 124 119 125 // Determine destination path 120 126 let dest_entry = if is_glob { 121 127 // For glob patterns, use filename in destination directory 122 128 let filename = rel_path.split('/').last().unwrap_or(&rel_path); 123 - dest.join(filename).map_err(to_shell_err(call.arguments_span()))? 129 + dest.join(filename) 130 + .map_err(to_shell_err(call.arguments_span()))? 124 131 } else { 125 132 // For single path, use destination as-is 126 133 dest.clone() ··· 128 135 129 136 match source_meta.file_type { 130 137 VfsFileType::File => move_file(&source, &dest_entry, call.arguments_span())?, 131 - VfsFileType::Directory => move_directory(&source, &dest_entry, call.arguments_span())?, 138 + VfsFileType::Directory => { 139 + move_directory(&source, &dest_entry, call.arguments_span())? 140 + } 132 141 } 133 142 } 134 143
+16 -13
src/cmd/open.rs
··· 1 1 use std::ops::Not; 2 2 3 3 use crate::{ 4 - cmd::glob::{expand_path, GlobOptions}, 4 + cmd::glob::{GlobOptions, expand_path}, 5 5 globals::{get_pwd, get_vfs}, 6 6 }; 7 - use std::sync::Arc; 8 7 use nu_command::{FromCsv, FromJson, FromOds, FromToml, FromTsv, FromXlsx, FromXml, FromYaml}; 9 8 use nu_engine::CallExt; 10 9 use nu_protocol::{ 11 - ByteStream, Category, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Type, Value, 10 + ByteStream, Category, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Type, 11 + Value, 12 12 engine::{Command, EngineState, Stack}, 13 13 }; 14 + use std::sync::Arc; 14 15 15 16 #[derive(Clone)] 16 17 pub struct Open; ··· 65 66 66 67 // Expand path (glob or single) into list of paths 67 68 let is_absolute = path_str.starts_with('/'); 68 - let base_path: Arc<vfs::VfsPath> = if is_absolute { 69 - get_vfs() 70 - } else { 71 - get_pwd() 72 - }; 69 + let base_path: Arc<vfs::VfsPath> = if is_absolute { get_vfs() } else { get_pwd() }; 73 70 74 71 let options = GlobOptions { 75 72 max_depth: None, ··· 127 124 match cmd.run(engine_state, stack, call, data) { 128 125 Ok(pipeline_data) => { 129 126 // Convert pipeline data to value 130 - pipeline_data.into_value(span).unwrap_or_else(|e| { 131 - Value::error(e, span) 132 - }) 127 + pipeline_data 128 + .into_value(span) 129 + .unwrap_or_else(|e| Value::error(e, span)) 133 130 } 134 131 Err(e) => Value::error(e, span), 135 132 } 136 133 } else { 137 - data.into_value(span).unwrap_or_else(|e| Value::error(e, span)) 134 + data.into_value(span) 135 + .unwrap_or_else(|e| Value::error(e, span)) 138 136 }; 139 137 results.push(value); 140 138 } ··· 154 152 } 155 153 156 154 // If single file, return the single result directly (for backward compatibility) 157 - if results.len() == 1 && !path_str.contains('*') && !path_str.contains('?') && !path_str.contains('[') && !path_str.contains("**") { 155 + if results.len() == 1 156 + && !path_str.contains('*') 157 + && !path_str.contains('?') 158 + && !path_str.contains('[') 159 + && !path_str.contains("**") 160 + { 158 161 match results.into_iter().next().unwrap() { 159 162 Value::Error { error, .. } => Err(*error), 160 163 val => Ok(PipelineData::Value(val, None)),
+3 -7
src/cmd/rm.rs
··· 1 1 use crate::{ 2 - cmd::glob::{expand_path, GlobOptions}, 2 + cmd::glob::{GlobOptions, expand_path}, 3 3 error::to_shell_err, 4 4 globals::{get_pwd, get_vfs}, 5 5 }; 6 - use std::sync::Arc; 7 6 use nu_engine::CallExt; 8 7 use nu_protocol::{ 9 8 Category, PipelineData, ShellError, Signature, SyntaxShape, Type, Value, 10 9 engine::{Command, EngineState, Stack}, 11 10 }; 11 + use std::sync::Arc; 12 12 use vfs::VfsFileType; 13 13 14 14 #[derive(Clone)] ··· 75 75 76 76 // Expand path (glob or single) into list of paths 77 77 let is_absolute = path_str.starts_with('/'); 78 - let base_path: Arc<vfs::VfsPath> = if is_absolute { 79 - get_vfs() 80 - } else { 81 - get_pwd() 82 - }; 78 + let base_path: Arc<vfs::VfsPath> = if is_absolute { get_vfs() } else { get_pwd() }; 83 79 84 80 let options = GlobOptions { 85 81 max_depth: None,
+7 -11
src/cmd/source_file.rs
··· 3 3 error::{CommandError, to_shell_err}, 4 4 globals::{get_pwd, get_vfs, print_to_console, set_pwd}, 5 5 }; 6 - use std::sync::Arc; 7 6 use nu_engine::{CallExt, get_eval_block_with_early_return}; 8 7 use nu_parser::parse; 9 8 use nu_protocol::{ 10 9 Category, PipelineData, ShellError, Signature, SyntaxShape, Type, Value, 11 10 engine::{Command, EngineState, Stack, StateWorkingSet}, 12 11 }; 12 + use std::sync::Arc; 13 13 14 14 #[derive(Clone)] 15 15 pub struct SourceFile; ··· 60 60 61 61 let pwd = get_pwd(); 62 62 let is_absolute = path_str.starts_with('/'); 63 - let base_path: Arc<vfs::VfsPath> = if is_absolute { 64 - get_vfs() 65 - } else { 66 - pwd.clone() 67 - }; 63 + let base_path: Arc<vfs::VfsPath> = if is_absolute { get_vfs() } else { pwd.clone() }; 68 64 69 65 // Check if it's a glob pattern (contains *, ?, [, or **) 70 - let is_glob = path_str.contains('*') 71 - || path_str.contains('?') 72 - || path_str.contains('[') 66 + let is_glob = path_str.contains('*') 67 + || path_str.contains('?') 68 + || path_str.contains('[') 73 69 || path_str.contains("**"); 74 70 75 71 let paths_to_source = if is_glob { 76 72 // Expand glob pattern 77 73 let options = crate::cmd::glob::GlobOptions { 78 74 max_depth: None, 79 - no_dirs: true, // Only source files, not directories 75 + no_dirs: true, // Only source files, not directories 80 76 no_files: false, 81 77 }; 82 78 glob_match(&path_str, base_path.clone(), options)? ··· 88 84 // Source each matching file 89 85 for rel_path in paths_to_source { 90 86 let full_path = base_path.join(&rel_path).map_err(to_shell_err(span))?; 91 - 87 + 92 88 let metadata = full_path.metadata().map_err(to_shell_err(span))?; 93 89 if metadata.file_type != vfs::VfsFileType::File { 94 90 continue;
+2 -2
src/lib.rs
··· 28 28 29 29 use crate::{ 30 30 cmd::{ 31 - Cd, Eval, Fetch, Glob, Job, JobKill, JobList, Ls, Mkdir, Mv, Open, Print, Pwd, Random, Rm, Save, 32 - SourceFile, Sys, 31 + Cd, Eval, Fetch, Glob, Job, JobKill, JobList, Ls, Mkdir, Mv, Open, Print, Pwd, Random, Rm, 32 + Save, SourceFile, Sys, 33 33 }, 34 34 default_context::add_shell_command_context, 35 35 globals::{InterruptBool, get_pwd, get_vfs, print_to_console, set_interrupt},