tangled
alpha
login
or
join now
ptr.pet
/
faunu
2
fork
atom
nushell on your web browser
nushell
wasm
terminal
2
fork
atom
overview
issues
pulls
pipelines
format
ptr.pet
2 months ago
80408401
5600d1b6
verified
This commit was signed with the committer's
known signature
.
ptr.pet
SSH Key Fingerprint:
SHA256:Abmvag+juovVufZTxyWY8KcVgrznxvBjQpJesv071Aw=
+93
-93
7 changed files
expand all
collapse all
unified
split
src
cmd
glob.rs
ls.rs
mv.rs
open.rs
rm.rs
source_file.rs
lib.rs
+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
-
90
90
+
91
91
// Collect matching paths
92
92
let mut matches = Vec::new();
93
93
-
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
114
-
let entry_path = current_path.join(&filename)
114
114
+
let entry_path =
115
115
+
current_path
116
116
+
.join(&filename)
117
117
+
.map_err(|e| ShellError::GenericError {
118
118
+
error: "path error".into(),
119
119
+
msg: e.to_string(),
120
120
+
span: None,
121
121
+
help: None,
122
122
+
inner: vec![],
123
123
+
})?;
124
124
+
125
125
+
// Build relative path from base
126
126
+
let new_relative = if current_relative_path.is_empty() {
127
127
+
filename.clone()
128
128
+
} else {
129
129
+
format!("{}/{}", current_relative_path, filename)
130
130
+
};
131
131
+
132
132
+
let metadata = entry_path
133
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
122
-
123
123
-
// Build relative path from base
124
124
-
let new_relative = if current_relative_path.is_empty() {
125
125
-
filename.clone()
126
126
-
} else {
127
127
-
format!("{}/{}", current_relative_path, filename)
128
128
-
};
129
129
-
130
130
-
let metadata = entry_path.metadata().map_err(|e| ShellError::GenericError {
131
131
-
error: "path error".into(),
132
132
-
msg: e.to_string(),
133
133
-
span: None,
134
134
-
help: None,
135
135
-
inner: vec![],
136
136
-
})?;
137
137
-
141
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
146
-
150
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
156
-
160
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
160
-
let should_recurse = is_recursive
164
164
+
let should_recurse = is_recursive
161
165
|| current_depth < max_depth
162
162
-
|| (normalized_pattern.contains('/') && current_depth < normalized_pattern.split('/').count());
163
163
-
166
166
+
|| (normalized_pattern.contains('/')
167
167
+
&& current_depth < normalized_pattern.split('/').count());
168
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
279
-
let base_path = if is_absolute {
280
280
-
get_vfs()
281
281
-
} else {
282
282
-
get_pwd()
283
283
-
};
284
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
291
-
292
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
296
-
let values = matches.into_iter().map(move |path| Value::string(path, span));
297
297
-
297
297
+
let values = matches
298
298
+
.into_iter()
299
299
+
.map(move |path| Value::string(path, span));
300
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
304
-
+8
-14
src/cmd/ls.rs
···
1
1
use std::time::{SystemTime, UNIX_EPOCH};
2
2
3
3
use crate::{
4
4
-
cmd::glob::{expand_path, GlobOptions},
4
4
+
cmd::glob::{GlobOptions, expand_path},
5
5
error::to_shell_err,
6
6
globals::{get_pwd, get_vfs},
7
7
};
8
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
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
81
-
let base_path: Arc<vfs::VfsPath> = if is_absolute {
82
82
-
get_vfs()
83
83
-
} else {
84
84
-
pwd.clone()
85
85
-
};
81
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
148
-
let entries = matches
149
149
-
.into_iter()
150
150
-
.flat_map(move |rel_path| {
151
151
-
make_record(&rel_path)
152
152
-
.transpose()
153
153
-
.map(|res| res.unwrap_or_else(|err| Value::error(err, span)))
154
154
-
});
144
144
+
let entries = matches.into_iter().flat_map(move |rel_path| {
145
145
+
make_record(&rel_path)
146
146
+
.transpose()
147
147
+
.map(|res| res.unwrap_or_else(|err| Value::error(err, span)))
148
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
4
-
cmd::glob::{expand_path, GlobOptions},
4
4
+
cmd::glob::{GlobOptions, expand_path},
5
5
error::to_shell_err,
6
6
globals::{get_pwd, get_vfs},
7
7
};
8
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
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
80
-
let base_path: Arc<vfs::VfsPath> = if is_absolute {
81
81
-
get_vfs()
82
82
-
} else {
83
83
-
get_pwd()
84
84
-
};
80
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
93
-
let is_glob = matches.len() > 1 || source_str.contains('*') || source_str.contains('?') || source_str.contains('[') || source_str.contains("**");
89
89
+
let is_glob = matches.len() > 1
90
90
+
|| source_str.contains('*')
91
91
+
|| source_str.contains('?')
92
92
+
|| source_str.contains('[')
93
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
102
-
let dest_meta = dest.metadata().map_err(to_shell_err(call.arguments_span()))?;
102
102
+
let dest_meta = dest
103
103
+
.metadata()
104
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
116
-
let source = base_path.join(&rel_path).map_err(to_shell_err(call.arguments_span()))?;
117
117
-
let source_meta = source.metadata().map_err(to_shell_err(call.arguments_span()))?;
118
118
+
let source = base_path
119
119
+
.join(&rel_path)
120
120
+
.map_err(to_shell_err(call.arguments_span()))?;
121
121
+
let source_meta = source
122
122
+
.metadata()
123
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
123
-
dest.join(filename).map_err(to_shell_err(call.arguments_span()))?
129
129
+
dest.join(filename)
130
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
131
-
VfsFileType::Directory => move_directory(&source, &dest_entry, call.arguments_span())?,
138
138
+
VfsFileType::Directory => {
139
139
+
move_directory(&source, &dest_entry, call.arguments_span())?
140
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
4
-
cmd::glob::{expand_path, GlobOptions},
4
4
+
cmd::glob::{GlobOptions, expand_path},
5
5
globals::{get_pwd, get_vfs},
6
6
};
7
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
11
-
ByteStream, Category, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
10
10
+
ByteStream, Category, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Type,
11
11
+
Value,
12
12
engine::{Command, EngineState, Stack},
13
13
};
14
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
68
-
let base_path: Arc<vfs::VfsPath> = if is_absolute {
69
69
-
get_vfs()
70
70
-
} else {
71
71
-
get_pwd()
72
72
-
};
69
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
130
-
pipeline_data.into_value(span).unwrap_or_else(|e| {
131
131
-
Value::error(e, span)
132
132
-
})
127
127
+
pipeline_data
128
128
+
.into_value(span)
129
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
137
-
data.into_value(span).unwrap_or_else(|e| Value::error(e, span))
134
134
+
data.into_value(span)
135
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
157
-
if results.len() == 1 && !path_str.contains('*') && !path_str.contains('?') && !path_str.contains('[') && !path_str.contains("**") {
155
155
+
if results.len() == 1
156
156
+
&& !path_str.contains('*')
157
157
+
&& !path_str.contains('?')
158
158
+
&& !path_str.contains('[')
159
159
+
&& !path_str.contains("**")
160
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
2
-
cmd::glob::{expand_path, GlobOptions},
2
2
+
cmd::glob::{GlobOptions, expand_path},
3
3
error::to_shell_err,
4
4
globals::{get_pwd, get_vfs},
5
5
};
6
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
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
78
-
let base_path: Arc<vfs::VfsPath> = if is_absolute {
79
79
-
get_vfs()
80
80
-
} else {
81
81
-
get_pwd()
82
82
-
};
78
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
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
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
63
-
let base_path: Arc<vfs::VfsPath> = if is_absolute {
64
64
-
get_vfs()
65
65
-
} else {
66
66
-
pwd.clone()
67
67
-
};
63
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
70
-
let is_glob = path_str.contains('*')
71
71
-
|| path_str.contains('?')
72
72
-
|| path_str.contains('[')
66
66
+
let is_glob = path_str.contains('*')
67
67
+
|| path_str.contains('?')
68
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
79
-
no_dirs: true, // Only source files, not directories
75
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
91
-
87
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
31
-
Cd, Eval, Fetch, Glob, Job, JobKill, JobList, Ls, Mkdir, Mv, Open, Print, Pwd, Random, Rm, Save,
32
32
-
SourceFile, Sys,
31
31
+
Cd, Eval, Fetch, Glob, Job, JobKill, JobList, Ls, Mkdir, Mv, Open, Print, Pwd, Random, Rm,
32
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},