forked from extern/nushell
open
, rm
, umv
, cp
, rm
and du
: Don't globs if inputs are variables or string interpolation (#11886)
# Description
This is a follow up to
https://github.com/nushell/nushell/pull/11621#issuecomment-1937484322
Also Fixes: #11838
## About the code change
It applys the same logic when we pass variables to external commands:
0487e9ffcb/crates/nu-command/src/system/run_external.rs (L162-L170)
That is: if user input dynamic things(like variables, sub-expression, or
string interpolation), it returns a quoted `NuPath`, then user input
won't be globbed
# User-Facing Changes
Given two input files: `a*c.txt`, `abc.txt`
* `let f = "a*c.txt"; rm $f` will remove one file: `a*c.txt`.
~* `let f = "a*c.txt"; rm --glob $f` will remove `a*c.txt` and
`abc.txt`~
* `let f: glob = "a*c.txt"; rm $f` will remove `a*c.txt` and `abc.txt`
## Rules about globbing with *variable*
Given two files: `a*c.txt`, `abc.txt`
| Cmd Type | example | Result |
| ----- | ------------------ | ------ |
| builtin | let f = "a*c.txt"; rm $f | remove `a*c.txt` |
| builtin | let f: glob = "a*c.txt"; rm $f | remove `a*c.txt` and
`abc.txt`
| builtin | let f = "a*c.txt"; rm ($f \| into glob) | remove `a*c.txt`
and `abc.txt`
| custom | def crm [f: glob] { rm $f }; let f = "a*c.txt"; crm $f |
remove `a*c.txt` and `abc.txt`
| custom | def crm [f: glob] { rm ($f \| into string) }; let f =
"a*c.txt"; crm $f | remove `a*c.txt`
| custom | def crm [f: string] { rm $f }; let f = "a*c.txt"; crm $f |
remove `a*c.txt`
| custom | def crm [f: string] { rm $f }; let f = "a*c.txt"; crm ($f \|
into glob) | remove `a*c.txt` and `abc.txt`
In general, if a variable is annotated with `glob` type, nushell will
expand glob pattern. Or else, we need to use `into | glob` to expand
glob pattern
# Tests + Formatting
Done
# After Submitting
I think `str glob-escape` command will be no-longer required. We can
remove it.
This commit is contained in:
133
crates/nu-command/src/conversions/into/glob.rs
Normal file
133
crates/nu-command/src/conversions/into/glob.rs
Normal file
@ -0,0 +1,133 @@
|
||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||
Type, Value,
|
||||
};
|
||||
|
||||
struct Arguments {
|
||||
cell_paths: Option<Vec<CellPath>>,
|
||||
}
|
||||
|
||||
impl CmdArgument for Arguments {
|
||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.cell_paths.take()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"into glob"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into glob")
|
||||
.input_output_types(vec![
|
||||
(Type::String, Type::Glob),
|
||||
(
|
||||
Type::List(Box::new(Type::String)),
|
||||
Type::List(Box::new(Type::Glob)),
|
||||
),
|
||||
(Type::Table(vec![]), Type::Table(vec![])),
|
||||
(Type::Record(vec![]), Type::Record(vec![])),
|
||||
])
|
||||
.allow_variants_without_examples(true) // https://github.com/nushell/nushell/issues/7032
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::CellPath,
|
||||
"For a data structure input, convert data at the given cell paths.",
|
||||
)
|
||||
.category(Category::Conversions)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Convert value to glob."
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["convert", "text"]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
glob_helper(engine_state, stack, call, input)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "convert string to glob",
|
||||
example: "'1234' | into glob",
|
||||
result: Some(Value::test_string("1234")),
|
||||
},
|
||||
Example {
|
||||
description: "convert filepath to string",
|
||||
example: "ls Cargo.toml | get name | into glob",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn glob_helper(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let cell_paths = call.rest(engine_state, stack, 0)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
let args = Arguments { cell_paths };
|
||||
match input {
|
||||
PipelineData::ExternalStream { stdout: None, .. } => {
|
||||
Ok(Value::glob(String::new(), false, head).into_pipeline_data())
|
||||
}
|
||||
PipelineData::ExternalStream {
|
||||
stdout: Some(stream),
|
||||
..
|
||||
} => {
|
||||
// TODO: in the future, we may want this to stream out, converting each to bytes
|
||||
let output = stream.into_string()?;
|
||||
Ok(Value::glob(output.item, false, head).into_pipeline_data())
|
||||
}
|
||||
_ => operate(action, args, input, head, engine_state.ctrlc.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
fn action(input: &Value, _args: &Arguments, span: Span) -> Value {
|
||||
match input {
|
||||
Value::String { val, .. } => Value::glob(val.to_string(), false, span),
|
||||
x => Value::error(
|
||||
ShellError::CantConvert {
|
||||
to_type: String::from("glob"),
|
||||
from_type: x.get_type().to_string(),
|
||||
span,
|
||||
help: None,
|
||||
},
|
||||
span,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ mod datetime;
|
||||
mod duration;
|
||||
mod filesize;
|
||||
mod float;
|
||||
mod glob;
|
||||
mod int;
|
||||
mod record;
|
||||
mod string;
|
||||
@ -19,6 +20,7 @@ pub use command::Into;
|
||||
pub use datetime::SubCommand as IntoDatetime;
|
||||
pub use duration::SubCommand as IntoDuration;
|
||||
pub use float::SubCommand as IntoFloat;
|
||||
pub use glob::SubCommand as IntoGlob;
|
||||
pub use int::SubCommand as IntoInt;
|
||||
pub use record::SubCommand as IntoRecord;
|
||||
pub use string::SubCommand as IntoString;
|
||||
|
@ -36,6 +36,7 @@ impl Command for SubCommand {
|
||||
(Type::Int, Type::String),
|
||||
(Type::Number, Type::String),
|
||||
(Type::String, Type::String),
|
||||
(Type::Glob, Type::String),
|
||||
(Type::Bool, Type::String),
|
||||
(Type::Filesize, Type::String),
|
||||
(Type::Date, Type::String),
|
||||
@ -202,6 +203,7 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
||||
Value::Bool { val, .. } => Value::string(val.to_string(), span),
|
||||
Value::Date { val, .. } => Value::string(val.format("%c").to_string(), span),
|
||||
Value::String { val, .. } => Value::string(val.to_string(), span),
|
||||
Value::Glob { val, .. } => Value::string(val.to_string(), span),
|
||||
|
||||
Value::Filesize { val: _, .. } => {
|
||||
Value::string(input.to_expanded_string(", ", config), span)
|
||||
|
@ -354,6 +354,7 @@ fn nu_value_to_sqlite_type(val: &Value) -> Result<&'static str, ShellError> {
|
||||
| Type::Range
|
||||
| Type::Record(_)
|
||||
| Type::Signature
|
||||
| Type::Glob
|
||||
| Type::Table(_) => Err(ShellError::OnlySupportsThisInputType {
|
||||
exp_input_type: "sql".into(),
|
||||
wrong_type: val.get_type().to_string(),
|
||||
|
@ -251,7 +251,7 @@ pub fn debug_string_without_formatting(value: &Value) -> String {
|
||||
)
|
||||
}
|
||||
Value::String { val, .. } => val.clone(),
|
||||
Value::QuotedString { val, .. } => val.clone(),
|
||||
Value::Glob { val, .. } => val.clone(),
|
||||
Value::List { vals: val, .. } => format!(
|
||||
"[{}]",
|
||||
val.iter()
|
||||
|
@ -303,6 +303,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
||||
IntoInt,
|
||||
IntoRecord,
|
||||
IntoString,
|
||||
IntoGlob,
|
||||
IntoValue,
|
||||
};
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
use super::util::opt_for_glob_pattern;
|
||||
use crate::{DirBuilder, DirInfo, FileInfo};
|
||||
use nu_engine::{current_dir, CallExt};
|
||||
use nu_glob::Pattern;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, IntoInterruptiblePipelineData, NuPath, PipelineData, ShellError, Signature,
|
||||
Category, Example, IntoInterruptiblePipelineData, NuGlob, PipelineData, ShellError, Signature,
|
||||
Span, Spanned, SyntaxShape, Type, Value,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
@ -14,7 +15,7 @@ pub struct Du;
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
pub struct DuArgs {
|
||||
path: Option<Spanned<NuPath>>,
|
||||
path: Option<Spanned<NuGlob>>,
|
||||
all: bool,
|
||||
deref: bool,
|
||||
exclude: Option<Spanned<String>>,
|
||||
@ -66,7 +67,7 @@ impl Command for Du {
|
||||
"Exclude files below this size",
|
||||
Some('m'),
|
||||
)
|
||||
.category(Category::Core)
|
||||
.category(Category::FileSystem)
|
||||
}
|
||||
|
||||
fn run(
|
||||
@ -96,7 +97,7 @@ impl Command for Du {
|
||||
let current_dir = current_dir(engine_state, stack)?;
|
||||
|
||||
let args = DuArgs {
|
||||
path: call.opt(engine_state, stack, 0)?,
|
||||
path: opt_for_glob_pattern(engine_state, stack, call, 0)?,
|
||||
all: call.has_flag(engine_state, stack, "all")?,
|
||||
deref: call.has_flag(engine_state, stack, "deref")?,
|
||||
exclude: call.get_flag(engine_state, stack, "exclude")?,
|
||||
@ -119,7 +120,7 @@ impl Command for Du {
|
||||
// The * pattern should never fail.
|
||||
None => nu_engine::glob_from(
|
||||
&Spanned {
|
||||
item: NuPath::UnQuoted("*".into()),
|
||||
item: NuGlob::Expand("*".into()),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
¤t_dir,
|
||||
|
@ -1,3 +1,4 @@
|
||||
use super::util::opt_for_glob_pattern;
|
||||
use crate::DirBuilder;
|
||||
use crate::DirInfo;
|
||||
use chrono::{DateTime, Local, LocalResult, TimeZone, Utc};
|
||||
@ -7,7 +8,7 @@ use nu_glob::{MatchOptions, Pattern};
|
||||
use nu_path::expand_to_real_path;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::NuPath;
|
||||
use nu_protocol::NuGlob;
|
||||
use nu_protocol::{
|
||||
Category, DataSource, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
||||
PipelineMetadata, Record, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||
@ -86,17 +87,16 @@ impl Command for Ls {
|
||||
let call_span = call.head;
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
|
||||
let pattern_arg: Option<Spanned<NuPath>> = call.opt(engine_state, stack, 0)?;
|
||||
|
||||
let pattern_arg = opt_for_glob_pattern(engine_state, stack, call, 0)?;
|
||||
let pattern_arg = {
|
||||
if let Some(path) = pattern_arg {
|
||||
match path.item {
|
||||
NuPath::Quoted(p) => Some(Spanned {
|
||||
item: NuPath::Quoted(nu_utils::strip_ansi_string_unlikely(p)),
|
||||
NuGlob::DoNotExpand(p) => Some(Spanned {
|
||||
item: NuGlob::DoNotExpand(nu_utils::strip_ansi_string_unlikely(p)),
|
||||
span: path.span,
|
||||
}),
|
||||
NuPath::UnQuoted(p) => Some(Spanned {
|
||||
item: NuPath::UnQuoted(nu_utils::strip_ansi_string_unlikely(p)),
|
||||
NuGlob::Expand(p) => Some(Spanned {
|
||||
item: NuGlob::Expand(nu_utils::strip_ansi_string_unlikely(p)),
|
||||
span: path.span,
|
||||
}),
|
||||
}
|
||||
@ -149,7 +149,7 @@ impl Command for Ls {
|
||||
p,
|
||||
p_tag,
|
||||
absolute_path,
|
||||
matches!(pat.item, NuPath::Quoted(_)),
|
||||
matches!(pat.item, NuGlob::DoNotExpand(_)),
|
||||
)
|
||||
}
|
||||
None => {
|
||||
@ -186,8 +186,8 @@ impl Command for Ls {
|
||||
};
|
||||
|
||||
let glob_path = Spanned {
|
||||
// It needs to be un-quoted, the relative logic is handled previously
|
||||
item: NuPath::UnQuoted(path.clone()),
|
||||
// use NeedExpand, the relative escaping logic is handled previously
|
||||
item: NuGlob::Expand(path.clone()),
|
||||
span: p_tag,
|
||||
};
|
||||
|
||||
|
@ -6,7 +6,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, NuPath, PipelineData, ShellError, Signature,
|
||||
Category, Example, IntoInterruptiblePipelineData, NuGlob, PipelineData, ShellError, Signature,
|
||||
Span, Spanned, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
@ -62,7 +62,7 @@ impl Command for Mv {
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
// TODO: handle invalid directory or insufficient permissions when moving
|
||||
let mut spanned_source: Spanned<NuPath> = call.req(engine_state, stack, 0)?;
|
||||
let mut spanned_source: Spanned<NuGlob> = call.req(engine_state, stack, 0)?;
|
||||
spanned_source.item = spanned_source.item.strip_ansi_string_unlikely();
|
||||
let spanned_destination: Spanned<String> = call.req(engine_state, stack, 1)?;
|
||||
let verbose = call.has_flag(engine_state, stack, "verbose")?;
|
||||
|
@ -1,10 +1,11 @@
|
||||
use super::util::get_rest_for_glob_pattern;
|
||||
use nu_engine::{current_dir, eval_block, CallExt};
|
||||
use nu_path::expand_to_real_path;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::util::BufferedReader;
|
||||
use nu_protocol::{
|
||||
Category, DataSource, Example, IntoInterruptiblePipelineData, NuPath, PipelineData,
|
||||
Category, DataSource, Example, IntoInterruptiblePipelineData, NuGlob, PipelineData,
|
||||
PipelineMetadata, RawStream, ShellError, Signature, Spanned, SyntaxShape, Type,
|
||||
};
|
||||
use std::io::BufReader;
|
||||
@ -58,7 +59,7 @@ impl Command for Open {
|
||||
let call_span = call.head;
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
let mut paths = call.rest::<Spanned<NuPath>>(engine_state, stack, 0)?;
|
||||
let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
|
||||
|
||||
if paths.is_empty() && call.rest_iter(0).next().is_none() {
|
||||
// try to use path from pipeline input if there were no positional or spread args
|
||||
@ -76,7 +77,7 @@ impl Command for Open {
|
||||
};
|
||||
|
||||
paths.push(Spanned {
|
||||
item: NuPath::UnQuoted(filename),
|
||||
item: NuGlob::Expand(filename),
|
||||
span,
|
||||
});
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ use std::io::ErrorKind;
|
||||
use std::os::unix::prelude::FileTypeExt;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::util::get_rest_for_glob_pattern;
|
||||
use super::util::try_interaction;
|
||||
|
||||
use nu_engine::env::current_dir;
|
||||
@ -14,7 +15,7 @@ use nu_path::expand_path_with;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, NuPath, PipelineData, ShellError, Signature,
|
||||
Category, Example, IntoInterruptiblePipelineData, NuGlob, PipelineData, ShellError, Signature,
|
||||
Span, Spanned, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
@ -126,7 +127,7 @@ fn rm(
|
||||
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
|
||||
let mut paths: Vec<Spanned<NuPath>> = call.rest(engine_state, stack, 0)?;
|
||||
let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
|
||||
|
||||
if paths.is_empty() {
|
||||
return Err(ShellError::MissingParameter {
|
||||
@ -166,8 +167,10 @@ fn rm(
|
||||
}
|
||||
let corrected_path = Spanned {
|
||||
item: match path.item {
|
||||
NuPath::Quoted(s) => NuPath::Quoted(nu_utils::strip_ansi_string_unlikely(s)),
|
||||
NuPath::UnQuoted(s) => NuPath::UnQuoted(nu_utils::strip_ansi_string_unlikely(s)),
|
||||
NuGlob::DoNotExpand(s) => {
|
||||
NuGlob::DoNotExpand(nu_utils::strip_ansi_string_unlikely(s))
|
||||
}
|
||||
NuGlob::Expand(s) => NuGlob::Expand(nu_utils::strip_ansi_string_unlikely(s)),
|
||||
},
|
||||
span: path.span,
|
||||
};
|
||||
|
@ -1,9 +1,9 @@
|
||||
use super::util::get_rest_for_glob_pattern;
|
||||
use nu_engine::{current_dir, CallExt};
|
||||
use nu_protocol::NuPath;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
|
||||
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
use uu_cp::{BackupMode, CopyMode, UpdateMode};
|
||||
@ -155,7 +155,7 @@ impl Command for UCp {
|
||||
target_os = "macos"
|
||||
)))]
|
||||
let reflink_mode = uu_cp::ReflinkMode::Never;
|
||||
let mut paths: Vec<Spanned<NuPath>> = call.rest(engine_state, stack, 0)?;
|
||||
let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
|
||||
if paths.is_empty() {
|
||||
return Err(ShellError::GenericError {
|
||||
error: "Missing file operand".into(),
|
||||
|
@ -1,11 +1,10 @@
|
||||
use super::util::get_rest_for_glob_pattern;
|
||||
use nu_engine::current_dir;
|
||||
use nu_engine::CallExt;
|
||||
use nu_path::{expand_path_with, expand_to_real_path};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, NuPath, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type,
|
||||
};
|
||||
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type};
|
||||
use std::ffi::OsString;
|
||||
use std::path::PathBuf;
|
||||
use uu_mv::{BackupMode, UpdateMode};
|
||||
@ -83,7 +82,7 @@ impl Command for UMv {
|
||||
};
|
||||
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
let mut paths: Vec<Spanned<NuPath>> = call.rest(engine_state, stack, 0)?;
|
||||
let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
|
||||
if paths.is_empty() {
|
||||
return Err(ShellError::GenericError {
|
||||
error: "Missing file operand".into(),
|
||||
|
@ -1,4 +1,12 @@
|
||||
use dialoguer::Input;
|
||||
use nu_engine::eval_expression;
|
||||
use nu_protocol::ast::Expr;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{EngineState, Stack},
|
||||
ShellError, Spanned, Value,
|
||||
};
|
||||
use nu_protocol::{FromValue, NuGlob, Type};
|
||||
use std::error::Error;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@ -200,3 +208,74 @@ pub mod users {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get rest arguments from given `call`, starts with `starting_pos`.
|
||||
///
|
||||
/// It's similar to `call.rest`, except that it always returns NuGlob. And if input argument has
|
||||
/// Type::Glob, the NuGlob is unquoted, which means it's required to expand.
|
||||
pub fn get_rest_for_glob_pattern(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
starting_pos: usize,
|
||||
) -> Result<Vec<Spanned<NuGlob>>, ShellError> {
|
||||
let mut output = vec![];
|
||||
|
||||
for result in call.rest_iter_flattened(starting_pos, |expr| {
|
||||
let result = eval_expression(engine_state, stack, expr);
|
||||
match result {
|
||||
Err(e) => Err(e),
|
||||
Ok(result) => {
|
||||
let span = result.span();
|
||||
// convert from string to quoted string if expr is a variable
|
||||
// or string interpolation
|
||||
match result {
|
||||
Value::String { val, .. }
|
||||
if matches!(
|
||||
&expr.expr,
|
||||
Expr::FullCellPath(_) | Expr::StringInterpolation(_)
|
||||
) =>
|
||||
{
|
||||
// should not expand if given input type is not glob.
|
||||
Ok(Value::glob(val, expr.ty != Type::Glob, span))
|
||||
}
|
||||
other => Ok(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
})? {
|
||||
output.push(FromValue::from_value(result)?);
|
||||
}
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/// Get optional arguments from given `call` with position `pos`.
|
||||
///
|
||||
/// It's similar to `call.opt`, except that it always returns NuGlob.
|
||||
pub fn opt_for_glob_pattern(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
pos: usize,
|
||||
) -> Result<Option<Spanned<NuGlob>>, ShellError> {
|
||||
if let Some(expr) = call.positional_nth(pos) {
|
||||
let result = eval_expression(engine_state, stack, expr)?;
|
||||
let result_span = result.span();
|
||||
let result = match result {
|
||||
Value::String { val, .. }
|
||||
if matches!(
|
||||
&expr.expr,
|
||||
Expr::FullCellPath(_) | Expr::StringInterpolation(_)
|
||||
) =>
|
||||
{
|
||||
// should quote if given input type is not glob.
|
||||
Value::glob(val, expr.ty != Type::Glob, result_span)
|
||||
}
|
||||
other => other,
|
||||
};
|
||||
FromValue::from_value(result).map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
@ -534,7 +534,7 @@ fn value_should_be_printed(
|
||||
| Value::Nothing { .. }
|
||||
| Value::Error { .. } => term_equals_value(term, &lower_value, span),
|
||||
Value::String { .. }
|
||||
| Value::QuotedString { .. }
|
||||
| Value::Glob { .. }
|
||||
| Value::List { .. }
|
||||
| Value::CellPath { .. }
|
||||
| Value::CustomValue { .. } => term_contains_value(term, &lower_value, span),
|
||||
|
@ -115,7 +115,7 @@ pub fn value_to_json_value(v: &Value) -> Result<nu_json::Value, ShellError> {
|
||||
Value::Int { val, .. } => nu_json::Value::I64(*val),
|
||||
Value::Nothing { .. } => nu_json::Value::Null,
|
||||
Value::String { val, .. } => nu_json::Value::String(val.to_string()),
|
||||
Value::QuotedString { val, .. } => nu_json::Value::String(val.to_string()),
|
||||
Value::Glob { val, .. } => nu_json::Value::String(val.to_string()),
|
||||
Value::CellPath { val, .. } => nu_json::Value::Array(
|
||||
val.members
|
||||
.iter()
|
||||
|
@ -279,7 +279,7 @@ pub fn value_to_string(
|
||||
// All strings outside data structures are quoted because they are in 'command position'
|
||||
// (could be mistaken for commands by the Nu parser)
|
||||
Value::String { val, .. } => Ok(escape_quote_string(val)),
|
||||
Value::QuotedString { val, .. } => Ok(escape_quote_string(val)),
|
||||
Value::Glob { val, .. } => Ok(escape_quote_string(val)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,7 +127,7 @@ fn local_into_string(value: Value, separator: &str, config: &Config) -> String {
|
||||
)
|
||||
}
|
||||
Value::String { val, .. } => val,
|
||||
Value::QuotedString { val, .. } => val,
|
||||
Value::Glob { val, .. } => val,
|
||||
Value::List { vals: val, .. } => val
|
||||
.into_iter()
|
||||
.map(|x| local_into_string(x, ", ", config))
|
||||
|
@ -57,9 +57,7 @@ fn helper(engine_state: &EngineState, v: &Value) -> Result<toml::Value, ShellErr
|
||||
}
|
||||
Value::Range { .. } => toml::Value::String("<Range>".to_string()),
|
||||
Value::Float { val, .. } => toml::Value::Float(*val),
|
||||
Value::String { val, .. } | Value::QuotedString { val, .. } => {
|
||||
toml::Value::String(val.clone())
|
||||
}
|
||||
Value::String { val, .. } | Value::Glob { val, .. } => toml::Value::String(val.clone()),
|
||||
Value::Record { val, .. } => {
|
||||
let mut m = toml::map::Map::new();
|
||||
for (k, v) in val {
|
||||
|
@ -52,7 +52,7 @@ pub fn value_to_yaml_value(v: &Value) -> Result<serde_yaml::Value, ShellError> {
|
||||
Value::Date { val, .. } => serde_yaml::Value::String(val.to_string()),
|
||||
Value::Range { .. } => serde_yaml::Value::Null,
|
||||
Value::Float { val, .. } => serde_yaml::Value::Number(serde_yaml::Number::from(*val)),
|
||||
Value::String { val, .. } | Value::QuotedString { val, .. } => {
|
||||
Value::String { val, .. } | Value::Glob { val, .. } => {
|
||||
serde_yaml::Value::String(val.clone())
|
||||
}
|
||||
Value::Record { val, .. } => {
|
||||
|
@ -2,7 +2,7 @@ use nu_cmd_base::hook::eval_hook;
|
||||
use nu_engine::env_to_strings;
|
||||
use nu_engine::eval_expression;
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::NuPath;
|
||||
use nu_protocol::NuGlob;
|
||||
use nu_protocol::{
|
||||
ast::{Call, Expr},
|
||||
did_you_mean,
|
||||
@ -730,9 +730,9 @@ fn trim_expand_and_apply_arg(
|
||||
}
|
||||
let cwd = PathBuf::from(cwd);
|
||||
if arg.item.contains('*') && run_glob_expansion {
|
||||
// we need to run glob expansion, so it's unquoted.
|
||||
// we need to run glob expansion, so it's NeedExpand.
|
||||
let path = Spanned {
|
||||
item: NuPath::UnQuoted(arg.item.clone()),
|
||||
item: NuGlob::Expand(arg.item.clone()),
|
||||
span: arg.span,
|
||||
};
|
||||
if let Ok((prefix, matches)) = nu_engine::glob_from(&path, &cwd, arg.span, None) {
|
||||
|
Reference in New Issue
Block a user