Merge branch 'main' into polars_categorical_2

This commit is contained in:
Jack Wright 2025-03-26 08:26:29 -07:00
commit 7fe312e53a
288 changed files with 3237 additions and 2035 deletions

52
.github/workflows/beta-test.yml vendored Normal file
View File

@ -0,0 +1,52 @@
name: Test on Beta Toolchain
# This workflow is made to run our tests on the beta toolchain to validate that
# the beta toolchain works.
# We do not intend to test here that we are working correctly but rather that
# the beta toolchain works correctly.
# The ci.yml handles our actual testing with our guarantees.
on:
schedule:
# If this workflow fails, GitHub notifications will go to the last person
# who edited this line.
# See: https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/monitoring-workflows/notifications-for-workflow-runs
- cron: '0 0 * * *' # Runs daily at midnight UTC
env:
NUSHELL_CARGO_PROFILE: ci
NU_LOG_LEVEL: DEBUG
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
cancel-in-progress: true
jobs:
build-and-test:
# this job is more for testing the beta toolchain and not our tests, so if
# this fails but the tests of the regular ci pass, then this is fine
continue-on-error: true
strategy:
fail-fast: true
matrix:
platform: [windows-latest, macos-latest, ubuntu-22.04]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
- run: rustup update beta
- name: Tests
run: cargo +beta test --workspace --profile ci --exclude nu_plugin_*
- name: Check for clean repo
shell: bash
run: |
if [ -n "$(git status --porcelain)" ]; then
echo "there are changes";
git status --porcelain
exit 1
else
echo "no changes in working directory";
fi

376
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@ license = "MIT"
name = "nu"
repository = "https://github.com/nushell/nushell"
rust-version = "1.83.0"
version = "0.102.1"
version = "0.103.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -92,7 +92,7 @@ filesize = "0.2"
filetime = "0.2"
heck = "0.5.0"
human-date-parser = "0.2.0"
indexmap = "2.7"
indexmap = "2.8"
indicatif = "0.17"
interprocess = "2.2.0"
is_executable = "1.0"
@ -104,13 +104,13 @@ lru = "0.12"
lscolors = { version = "0.17", default-features = false }
lsp-server = "0.7.8"
lsp-types = { version = "0.97.0", features = ["proposed"] }
lsp-textdocument = "0.4.1"
lsp-textdocument = "0.4.2"
mach2 = "0.4"
md5 = { version = "0.10", package = "md-5" }
miette = "7.5"
mime = "0.3.17"
mime_guess = "2.0"
mockito = { version = "1.6", default-features = false }
mockito = { version = "1.7", default-features = false }
multipart-rs = "0.1.13"
native-tls = "0.2"
nix = { version = "0.29", default-features = false }
@ -140,7 +140,7 @@ getrandom = "0.2" # pick same version that rand requires
rand_chacha = "0.3.1"
ratatui = "0.29"
rayon = "1.10"
reedline = "0.38.0"
reedline = "0.39.0"
rmp = "0.8"
rmp-serde = "1.3"
roxmltree = "0.20"
@ -161,7 +161,7 @@ syn = "2.0"
sysinfo = "0.33"
tabled = { version = "0.17.0", default-features = false }
tempfile = "3.15"
titlecase = "3.0"
titlecase = "3.4"
toml = "0.8"
trash = "5.2"
update-informer = { version = "1.2.0", default-features = false, features = ["github", "native-tls", "ureq"] }
@ -170,15 +170,15 @@ unicode-segmentation = "1.12"
unicode-width = "0.2"
ureq = { version = "2.12", default-features = false }
url = "2.2"
uu_cp = "0.0.29"
uu_mkdir = "0.0.29"
uu_mktemp = "0.0.29"
uu_mv = "0.0.29"
uu_touch = "0.0.29"
uu_whoami = "0.0.29"
uu_uname = "0.0.29"
uucore = "0.0.29"
uuid = "1.12.0"
uu_cp = "0.0.30"
uu_mkdir = "0.0.30"
uu_mktemp = "0.0.30"
uu_mv = "0.0.30"
uu_touch = "0.0.30"
uu_whoami = "0.0.30"
uu_uname = "0.0.30"
uucore = "0.0.30"
uuid = "1.16.0"
v_htmlescape = "0.15.0"
wax = "0.6"
web-time = "1.1.0"
@ -197,22 +197,22 @@ unchecked_duration_subtraction = "warn"
workspace = true
[dependencies]
nu-cli = { path = "./crates/nu-cli", version = "0.102.1" }
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.102.1" }
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.102.1" }
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.102.1", optional = true }
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.102.1" }
nu-command = { path = "./crates/nu-command", version = "0.102.1" }
nu-engine = { path = "./crates/nu-engine", version = "0.102.1" }
nu-explore = { path = "./crates/nu-explore", version = "0.102.1" }
nu-lsp = { path = "./crates/nu-lsp/", version = "0.102.1" }
nu-parser = { path = "./crates/nu-parser", version = "0.102.1" }
nu-path = { path = "./crates/nu-path", version = "0.102.1" }
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.102.1" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.102.1" }
nu-std = { path = "./crates/nu-std", version = "0.102.1" }
nu-system = { path = "./crates/nu-system", version = "0.102.1" }
nu-utils = { path = "./crates/nu-utils", version = "0.102.1" }
nu-cli = { path = "./crates/nu-cli", version = "0.103.1" }
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.103.1" }
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.103.1" }
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.103.1", optional = true }
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.103.1" }
nu-command = { path = "./crates/nu-command", version = "0.103.1" }
nu-engine = { path = "./crates/nu-engine", version = "0.103.1" }
nu-explore = { path = "./crates/nu-explore", version = "0.103.1" }
nu-lsp = { path = "./crates/nu-lsp/", version = "0.103.1" }
nu-parser = { path = "./crates/nu-parser", version = "0.103.1" }
nu-path = { path = "./crates/nu-path", version = "0.103.1" }
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.103.1" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.103.1" }
nu-std = { path = "./crates/nu-std", version = "0.103.1" }
nu-system = { path = "./crates/nu-system", version = "0.103.1" }
nu-utils = { path = "./crates/nu-utils", version = "0.103.1" }
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
crossterm = { workspace = true }
@ -220,7 +220,6 @@ ctrlc = { workspace = true }
dirs = { workspace = true }
log = { workspace = true }
miette = { workspace = true, features = ["fancy-no-backtrace", "fancy"] }
mimalloc = { version = "0.1.42", default-features = false, optional = true }
multipart-rs = { workspace = true }
serde_json = { workspace = true }
simplelog = "0.12"
@ -242,9 +241,9 @@ nix = { workspace = true, default-features = false, features = [
] }
[dev-dependencies]
nu-test-support = { path = "./crates/nu-test-support", version = "0.102.1" }
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.102.1" }
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.102.1" }
nu-test-support = { path = "./crates/nu-test-support", version = "0.103.1" }
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.103.1" }
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.103.1" }
assert_cmd = "2.0"
dirs = { workspace = true }
tango-bench = "0.6"
@ -274,7 +273,6 @@ default = [
"plugin",
"trash-support",
"sqlite",
"mimalloc",
]
stable = ["default"]
# NOTE: individual features are also passed to `nu-cmd-lang` that uses them to generate the feature matrix in the `version` command
@ -283,7 +281,6 @@ stable = ["default"]
# otherwise the system version will be used. Not enabled by default because it takes a while to build
static-link-openssl = ["dep:openssl", "nu-cmd-lang/static-link-openssl"]
mimalloc = ["nu-cmd-lang/mimalloc", "dep:mimalloc"]
# Optional system clipboard support in `reedline`, this behavior has problematic compatibility with some systems.
# Missing X server/ Wayland can cause issues
system-clipboard = [
@ -326,7 +323,7 @@ bench = false
# To use a development version of a dependency please use a global override here
# changing versions in each sub-crate of the workspace is tedious
[patch.crates-io]
reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
# reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}
# Run all benchmarks with `cargo bench`

View File

@ -5,28 +5,28 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
edition = "2021"
license = "MIT"
name = "nu-cli"
version = "0.102.1"
version = "0.103.1"
[lib]
bench = false
[dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.102.1" }
nu-command = { path = "../nu-command", version = "0.102.1" }
nu-test-support = { path = "../nu-test-support", version = "0.102.1" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.103.1" }
nu-command = { path = "../nu-command", version = "0.103.1" }
nu-test-support = { path = "../nu-test-support", version = "0.103.1" }
rstest = { workspace = true, default-features = false }
tempfile = { workspace = true }
[dependencies]
nu-cmd-base = { path = "../nu-cmd-base", version = "0.102.1" }
nu-engine = { path = "../nu-engine", version = "0.102.1", features = ["os"] }
nu-glob = { path = "../nu-glob", version = "0.102.1" }
nu-path = { path = "../nu-path", version = "0.102.1" }
nu-parser = { path = "../nu-parser", version = "0.102.1" }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.102.1", optional = true }
nu-protocol = { path = "../nu-protocol", version = "0.102.1", features = ["os"] }
nu-utils = { path = "../nu-utils", version = "0.102.1" }
nu-color-config = { path = "../nu-color-config", version = "0.102.1" }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.103.1" }
nu-engine = { path = "../nu-engine", version = "0.103.1", features = ["os"] }
nu-glob = { path = "../nu-glob", version = "0.103.1" }
nu-path = { path = "../nu-path", version = "0.103.1" }
nu-parser = { path = "../nu-parser", version = "0.103.1" }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.103.1", optional = true }
nu-protocol = { path = "../nu-protocol", version = "0.103.1", features = ["os"] }
nu-utils = { path = "../nu-utils", version = "0.103.1" }
nu-color-config = { path = "../nu-color-config", version = "0.103.1" }
nu-ansi-term = { workspace = true }
reedline = { workspace = true, features = ["bashisms", "sqlite"] }

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct CommandlineEdit;
impl Command for SubCommand {
impl Command for CommandlineEdit {
fn name(&self) -> &str {
"commandline edit"
}

View File

@ -2,9 +2,9 @@ use nu_engine::command_prelude::*;
use unicode_segmentation::UnicodeSegmentation;
#[derive(Clone)]
pub struct SubCommand;
pub struct CommandlineGetCursor;
impl Command for SubCommand {
impl Command for CommandlineGetCursor {
fn name(&self) -> &str {
"commandline get-cursor"
}

View File

@ -4,6 +4,6 @@ mod get_cursor;
mod set_cursor;
pub use commandline_::Commandline;
pub use edit::SubCommand as CommandlineEdit;
pub use get_cursor::SubCommand as CommandlineGetCursor;
pub use set_cursor::SubCommand as CommandlineSetCursor;
pub use edit::CommandlineEdit;
pub use get_cursor::CommandlineGetCursor;
pub use set_cursor::CommandlineSetCursor;

View File

@ -3,9 +3,9 @@ use nu_engine::command_prelude::*;
use unicode_segmentation::UnicodeSegmentation;
#[derive(Clone)]
pub struct SubCommand;
pub struct CommandlineSetCursor;
impl Command for SubCommand {
impl Command for CommandlineSetCursor {
fn name(&self) -> &str {
"commandline set-cursor"
}

View File

@ -307,9 +307,8 @@ impl NuCompleter {
let need_externals = !prefix_str.contains(' ');
let need_internals = !prefix_str.starts_with('^');
let mut span = element_expression.span;
span.end = std::cmp::min(span.end, pos + 1);
if !need_internals {
span = Span::new(span.start + 1, span.end)
span.start += 1;
};
suggestions.extend(self.command_completion_helper(
working_set,

View File

@ -65,10 +65,11 @@ fn complete_rec(
for entry in result.filter_map(|e| e.ok()) {
let entry_name = entry.file_name().to_string_lossy().into_owned();
let entry_isdir = entry.path().is_dir() && !entry.path().is_symlink();
let entry_isdir = entry.path().is_dir();
let mut built = built.clone();
built.parts.push(entry_name.clone());
built.isdir = entry_isdir;
// Symlinks to directories shouldn't have a trailing slash (#13275)
built.isdir = entry_isdir && !entry.path().is_symlink();
if !want_directory || entry_isdir {
matcher.add(entry_name.clone(), (entry_name, built));
@ -198,10 +199,9 @@ pub fn complete_item(
let ls_colors = (engine_state.config.completions.use_ls_colors
&& engine_state.config.use_ansi_coloring.get(engine_state))
.then(|| {
let ls_colors_env_str = match stack.get_env_var(engine_state, "LS_COLORS") {
Some(v) => env_to_string("LS_COLORS", v, engine_state, stack).ok(),
None => None,
};
let ls_colors_env_str = stack
.get_env_var(engine_state, "LS_COLORS")
.and_then(|v| env_to_string("LS_COLORS", v, engine_state, stack).ok());
get_ls_colors(ls_colors_env_str)
});
@ -263,19 +263,16 @@ pub fn complete_item(
}
let is_dir = p.isdir;
let path = original_cwd.apply(p, path_separator);
let real_path = expand_to_real_path(&path);
let metadata = std::fs::symlink_metadata(&real_path).ok();
let style = ls_colors.as_ref().map(|lsc| {
lsc.style_for_path_with_metadata(
&path,
std::fs::symlink_metadata(expand_to_real_path(&path))
.ok()
.as_ref(),
)
.map(lscolors::Style::to_nu_ansi_term_style)
.unwrap_or_default()
lsc.style_for_path_with_metadata(&real_path, metadata.as_ref())
.map(lscolors::Style::to_nu_ansi_term_style)
.unwrap_or_default()
});
FileSuggestion {
span,
path: escape_path(path, want_directory),
path: escape_path(path),
style,
is_dir,
}
@ -284,30 +281,30 @@ pub fn complete_item(
}
// Fix files or folders with quotes or hashes
pub fn escape_path(path: String, dir: bool) -> String {
pub fn escape_path(path: String) -> String {
// make glob pattern have the highest priority.
if nu_glob::is_glob(path.as_str()) {
if nu_glob::is_glob(path.as_str()) || path.contains('`') {
// expand home `~` for https://github.com/nushell/nushell/issues/13905
let pathbuf = nu_path::expand_tilde(path);
let path = pathbuf.to_string_lossy();
return if path.contains('\'') {
// decide to use double quote, also need to escape `"` in path
// or else users can't do anything with completed path either.
format!("\"{}\"", path.replace('"', r#"\""#))
if path.contains('\'') {
// decide to use double quotes
// Path as Debug will do the escaping for `"`, `\`
format!("{:?}", path)
} else {
format!("'{path}'")
};
}
let filename_contaminated = !dir && path.contains(['\'', '"', ' ', '#', '(', ')']);
let dirname_contaminated = dir && path.contains(['\'', '"', ' ', '#']);
let maybe_flag = path.starts_with('-');
let maybe_variable = path.starts_with('$');
let maybe_number = path.parse::<f64>().is_ok();
if filename_contaminated || dirname_contaminated || maybe_flag || maybe_variable || maybe_number
{
format!("`{path}`")
}
} else {
path
let contaminated =
path.contains(['\'', '"', ' ', '#', '(', ')', '{', '}', '[', ']', '|', ';']);
let maybe_flag = path.starts_with('-');
let maybe_variable = path.starts_with('$');
let maybe_number = path.parse::<f64>().is_ok();
if contaminated || maybe_flag || maybe_variable || maybe_number {
format!("`{path}`")
} else {
path
}
}
}

View File

@ -5,7 +5,7 @@ use nu_engine::eval_call;
use nu_protocol::{
ast::{Argument, Call, Expr, Expression},
debugger::WithoutDebug,
engine::{Stack, StateWorkingSet},
engine::{EngineState, Stack, StateWorkingSet},
DeclId, PipelineData, Span, Type, Value,
};
use std::collections::HashMap;
@ -42,28 +42,37 @@ impl<T: Completer> Completer for CustomCompletion<T> {
) -> Vec<SemanticSuggestion> {
// Call custom declaration
let mut stack_mut = stack.clone();
let result = eval_call::<WithoutDebug>(
working_set.permanent_state,
&mut stack_mut,
&Call {
decl_id: self.decl_id,
head: span,
arguments: vec![
Argument::Positional(Expression::new_unknown(
Expr::String(self.line.clone()),
Span::unknown(),
Type::String,
)),
Argument::Positional(Expression::new_unknown(
Expr::Int(self.line_pos as i64),
Span::unknown(),
Type::Int,
)),
],
parser_info: HashMap::new(),
},
PipelineData::empty(),
);
let mut eval = |engine_state: &EngineState| {
eval_call::<WithoutDebug>(
engine_state,
&mut stack_mut,
&Call {
decl_id: self.decl_id,
head: span,
arguments: vec![
Argument::Positional(Expression::new_unknown(
Expr::String(self.line.clone()),
Span::unknown(),
Type::String,
)),
Argument::Positional(Expression::new_unknown(
Expr::Int(self.line_pos as i64),
Span::unknown(),
Type::Int,
)),
],
parser_info: HashMap::new(),
},
PipelineData::empty(),
)
};
let result = if self.decl_id.get() < working_set.permanent_state.num_decls() {
eval(working_set.permanent_state)
} else {
let mut engine_state = working_set.permanent_state.clone();
let _ = engine_state.merge_delta(working_set.delta.clone());
eval(&engine_state)
};
let mut completion_options = orig_options.clone();
let mut should_sort = true;

View File

@ -37,6 +37,15 @@ fn common_comparison_ops() -> Vec<OperatorItem> {
]
}
fn all_ops_for_immutable() -> Vec<OperatorItem> {
ast::Comparison::iter()
.map(operator_to_item)
.chain(ast::Math::iter().map(operator_to_item))
.chain(ast::Boolean::iter().map(operator_to_item))
.chain(ast::Bits::iter().map(operator_to_item))
.collect()
}
fn collection_comparison_ops() -> Vec<OperatorItem> {
let mut ops = common_comparison_ops();
ops.push(operator_to_item(Comparison::Has));
@ -72,6 +81,10 @@ fn bit_ops() -> Vec<OperatorItem> {
ast::Bits::iter().map(operator_to_item).collect()
}
fn all_assignment_ops() -> Vec<OperatorItem> {
ast::Assignment::iter().map(operator_to_item).collect()
}
fn numeric_assignment_ops() -> Vec<OperatorItem> {
ast::Assignment::iter()
.filter(|op| !matches!(op, ast::Assignment::ConcatenateAssign))
@ -154,7 +167,7 @@ fn ops_by_value(value: &Value, mutable: bool) -> Vec<OperatorItem> {
Value::Filesize { .. } | Value::Duration { .. } => valid_value_with_unit_ops(),
Value::Range { .. } | Value::Record { .. } => collection_comparison_ops(),
Value::List { .. } => valid_list_ops(),
_ => common_comparison_ops(),
_ => all_ops_for_immutable(),
};
if mutable {
ops.extend(match value {
@ -165,7 +178,11 @@ fn ops_by_value(value: &Value, mutable: bool) -> Vec<OperatorItem> {
Value::String { .. } | Value::Binary { .. } | Value::List { .. } => {
concat_assignment_ops()
}
_ => vec![operator_to_item(ast::Assignment::Assign)],
Value::Bool { .. }
| Value::Date { .. }
| Value::Range { .. }
| Value::Record { .. } => vec![operator_to_item(ast::Assignment::Assign)],
_ => all_assignment_ops(),
})
}
ops
@ -223,7 +240,7 @@ impl Completer for OperatorCompletion<'_> {
needs_assignment_ops = false;
ops_by_value(&value, mutable)
}
_ => common_comparison_ops(),
_ => all_ops_for_immutable(),
},
_ => common_comparison_ops(),
};
@ -233,6 +250,7 @@ impl Completer for OperatorCompletion<'_> {
Type::Int | Type::Float | Type::Number => numeric_assignment_ops(),
Type::Filesize | Type::Duration => numeric_assignment_ops(),
Type::String | Type::Binary | Type::List(_) => concat_assignment_ops(),
Type::Any => all_assignment_ops(),
_ => vec![operator_to_item(ast::Assignment::Assign)],
});
}

View File

@ -307,7 +307,12 @@ fn custom_arguments_and_subcommands() {
let completion_str = "foo test";
let suggestions = completer.complete(completion_str, completion_str.len());
// including both subcommand and directory completions
let expected = ["foo test bar".into(), folder("test_a"), folder("test_b")];
let expected = [
"foo test bar".into(),
folder("test_a"),
file("test_a_symlink"),
folder("test_b"),
];
match_suggestions_by_string(&expected, &suggestions);
}
@ -345,6 +350,21 @@ fn custom_arguments_vs_subcommands() {
match_suggestions(&expected, &suggestions);
}
#[test]
fn custom_completions_defined_inline() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
let completion_str = "def animals [] { [cat dog] }
export def say [
animal: string@animals
] { }; say ";
let suggestions = completer.complete(completion_str, completion_str.len());
// including only subcommand completions
let expected: Vec<_> = vec!["cat", "dog"];
match_suggestions(&expected, &suggestions);
}
/// External command only if starts with `^`
#[test]
fn external_commands_only() {
@ -891,6 +911,7 @@ fn partial_completions() {
folder(dir.join("partial-a")),
folder(dir.join("partial-b")),
folder(dir.join("partial-c")),
format!("`{}`", folder(dir.join("partial-d("))),
];
// Match the results
@ -933,6 +954,7 @@ fn partial_completions() {
file(dir.join("partial-b").join("hello_b")),
file(dir.join("partial-b").join("hi_b")),
file(dir.join("partial-c").join("hello_c")),
format!("`{}`", file(dir.join("partial-d(").join(".gitkeep"))),
];
// Match the results
@ -980,6 +1002,15 @@ fn partial_completions() {
.join("final_partial")
.join("somefile"),
),
format!(
"`{}`",
file(
dir.join("partial-d(")
.join("..")
.join("final_partial")
.join("somefile"),
)
),
];
// Match the results
@ -1060,6 +1091,16 @@ fn partial_completion_with_dot_expansions() {
.join("final_partial")
.join("somefile"),
),
format!(
"`{}`",
file(
dir.join("partial-d(")
.join("...")
.join("partial_completions")
.join("final_partial")
.join("somefile"),
)
),
];
// Match the results
@ -1384,6 +1425,9 @@ fn file_completion_quoted() {
"`-inf`",
"`4.2`",
"\'[a] bc.txt\'",
"`curly-bracket_{.txt`",
"`semicolon_;.txt`",
"'square-bracket_[.txt'",
"`te st.txt`",
"`te#st.txt`",
"`te'st.txt`",
@ -1492,6 +1536,7 @@ fn folder_with_directorycompletions() {
folder(dir.join("another")),
folder(dir.join("directory_completion")),
folder(dir.join("test_a")),
file(dir.join("test_a_symlink")),
folder(dir.join("test_b")),
folder(dir.join(".hidden_folder")),
];
@ -1594,6 +1639,12 @@ fn folder_with_directorycompletions_with_three_trailing_dots() {
.join("...")
.join("test_a"),
),
file(
dir.join("directory_completion")
.join("folder_inside_folder")
.join("...")
.join("test_a_symlink"),
),
folder(
dir.join("directory_completion")
.join("folder_inside_folder")
@ -1666,6 +1717,13 @@ fn folder_with_directorycompletions_do_not_collapse_dots() {
.join("..")
.join("test_a"),
),
file(
dir.join("directory_completion")
.join("folder_inside_folder")
.join("..")
.join("..")
.join("test_a_symlink"),
),
folder(
dir.join("directory_completion")
.join("folder_inside_folder")
@ -2345,6 +2403,13 @@ fn assignment_operator_completions(mut custom_completer: NuCompleter) {
let expected: Vec<_> = vec!["++", "++="];
let suggestions = custom_completer.complete("$env.config.keybindings +", 25);
match_suggestions(&expected, &suggestions);
// all operators for type any
let suggestions = custom_completer.complete("ls | where name ", 16);
assert_eq!(30, suggestions.len());
let expected: Vec<_> = vec!["starts-with"];
let suggestions = custom_completer.complete("ls | where name starts", 22);
match_suggestions(&expected, &suggestions);
}
#[test]

View File

@ -5,7 +5,7 @@ edition = "2021"
license = "MIT"
name = "nu-cmd-base"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base"
version = "0.102.1"
version = "0.103.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -13,10 +13,10 @@ version = "0.102.1"
workspace = true
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.102.1", default-features = false }
nu-parser = { path = "../nu-parser", version = "0.102.1" }
nu-path = { path = "../nu-path", version = "0.102.1" }
nu-protocol = { path = "../nu-protocol", version = "0.102.1", default-features = false }
nu-engine = { path = "../nu-engine", version = "0.103.1", default-features = false }
nu-parser = { path = "../nu-parser", version = "0.103.1" }
nu-path = { path = "../nu-path", version = "0.103.1" }
nu-protocol = { path = "../nu-protocol", version = "0.103.1", default-features = false }
indexmap = { workspace = true }
miette = { workspace = true }

View File

@ -5,7 +5,7 @@ edition = "2021"
license = "MIT"
name = "nu-cmd-extra"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-extra"
version = "0.102.1"
version = "0.103.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -16,13 +16,13 @@ bench = false
workspace = true
[dependencies]
nu-cmd-base = { path = "../nu-cmd-base", version = "0.102.1" }
nu-engine = { path = "../nu-engine", version = "0.102.1", default-features = false }
nu-json = { version = "0.102.1", path = "../nu-json" }
nu-parser = { path = "../nu-parser", version = "0.102.1" }
nu-pretty-hex = { version = "0.102.1", path = "../nu-pretty-hex" }
nu-protocol = { path = "../nu-protocol", version = "0.102.1", default-features = false }
nu-utils = { path = "../nu-utils", version = "0.102.1", default-features = false }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.103.1" }
nu-engine = { path = "../nu-engine", version = "0.103.1", default-features = false }
nu-json = { version = "0.103.1", path = "../nu-json" }
nu-parser = { path = "../nu-parser", version = "0.103.1" }
nu-pretty-hex = { version = "0.103.1", path = "../nu-pretty-hex" }
nu-protocol = { path = "../nu-protocol", version = "0.103.1", default-features = false }
nu-utils = { path = "../nu-utils", version = "0.103.1", default-features = false }
# Potential dependencies for extras
heck = { workspace = true }
@ -37,6 +37,6 @@ itertools = { workspace = true }
mime = { workspace = true }
[dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.102.1" }
nu-command = { path = "../nu-command", version = "0.102.1" }
nu-test-support = { path = "../nu-test-support", version = "0.102.1" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.103.1" }
nu-command = { path = "../nu-command", version = "0.103.1" }
nu-test-support = { path = "../nu-test-support", version = "0.103.1" }

View File

@ -1,120 +0,0 @@
use nu_engine::command_prelude::*;
use nu_protocol::{report_parse_warning, ParseWarning};
#[derive(Clone)]
pub struct BitsInto;
impl Command for BitsInto {
fn name(&self) -> &str {
"into bits"
}
fn signature(&self) -> Signature {
Signature::build("into bits")
.input_output_types(vec![
(Type::Binary, Type::String),
(Type::Int, Type::String),
(Type::Filesize, Type::String),
(Type::Duration, Type::String),
(Type::String, Type::String),
(Type::Bool, Type::String),
(Type::table(), Type::table()),
(Type::record(), Type::record()),
])
.allow_variants_without_examples(true) // TODO: supply exhaustive examples
.rest(
"rest",
SyntaxShape::CellPath,
"For a data structure input, convert data at the given cell paths.",
)
.category(Category::Deprecated)
}
fn description(&self) -> &str {
"Convert value to a binary string."
}
fn search_terms(&self) -> Vec<&str> {
vec![]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
report_parse_warning(
&StateWorkingSet::new(engine_state),
&ParseWarning::DeprecatedWarning {
old_command: "into bits".into(),
new_suggestion: "use `format bits`".into(),
span: head,
url: "`help format bits`".into(),
},
);
crate::extra::strings::format::format_bits(engine_state, stack, call, input)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "convert a binary value into a string, padded to 8 places with 0s",
example: "0x[1] | into bits",
result: Some(Value::string("00000001",
Span::test_data(),
)),
},
Example {
description: "convert an int into a string, padded to 8 places with 0s",
example: "1 | into bits",
result: Some(Value::string("00000001",
Span::test_data(),
)),
},
Example {
description: "convert a filesize value into a string, padded to 8 places with 0s",
example: "1b | into bits",
result: Some(Value::string("00000001",
Span::test_data(),
)),
},
Example {
description: "convert a duration value into a string, padded to 8 places with 0s",
example: "1ns | into bits",
result: Some(Value::string("00000001",
Span::test_data(),
)),
},
Example {
description: "convert a boolean value into a string, padded to 8 places with 0s",
example: "true | into bits",
result: Some(Value::string("00000001",
Span::test_data(),
)),
},
Example {
description: "convert a string into a raw binary string, padded with 0s to 8 places",
example: "'nushell.sh' | into bits",
result: Some(Value::string("01101110 01110101 01110011 01101000 01100101 01101100 01101100 00101110 01110011 01101000",
Span::test_data(),
)),
},
]
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(BitsInto {})
}
}

View File

@ -1,6 +1,5 @@
mod and;
mod bits_;
mod into;
mod not;
mod or;
mod rotate_left;
@ -11,7 +10,6 @@ mod xor;
pub use and::BitsAnd;
pub use bits_::Bits;
pub use into::BitsInto;
pub use not::BitsNot;
pub use or::BitsOr;
pub use rotate_left::BitsRol;

View File

@ -1,74 +0,0 @@
use nu_engine::command_prelude::*;
use nu_protocol::{report_parse_warning, ParseWarning};
#[derive(Clone)]
pub struct Fmt;
impl Command for Fmt {
fn name(&self) -> &str {
"fmt"
}
fn description(&self) -> &str {
"Format a number."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("fmt")
.input_output_types(vec![(Type::Number, Type::record())])
.category(Category::Deprecated)
}
fn search_terms(&self) -> Vec<&str> {
vec![]
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Get a record containing multiple formats for the number 42",
example: "42 | fmt",
result: Some(Value::test_record(record! {
"binary" => Value::test_string("0b101010"),
"debug" => Value::test_string("42"),
"display" => Value::test_string("42"),
"lowerexp" => Value::test_string("4.2e1"),
"lowerhex" => Value::test_string("0x2a"),
"octal" => Value::test_string("0o52"),
"upperexp" => Value::test_string("4.2E1"),
"upperhex" => Value::test_string("0x2A"),
})),
}]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
report_parse_warning(
&StateWorkingSet::new(engine_state),
&ParseWarning::DeprecatedWarning {
old_command: "fmt".into(),
new_suggestion: "use `format number`".into(),
span: head,
url: "`help format number`".into(),
},
);
crate::extra::strings::format::format_number(engine_state, stack, call, input)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Fmt {})
}
}

View File

@ -1,3 +0,0 @@
mod fmt;
pub(crate) use fmt::Fmt;

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathArcCos;
impl Command for SubCommand {
impl Command for MathArcCos {
fn name(&self) -> &str {
"math arccos"
}
@ -114,6 +114,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathArcCos {})
}
}

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathArcCosH;
impl Command for SubCommand {
impl Command for MathArcCosH {
fn name(&self) -> &str {
"math arccosh"
}
@ -100,6 +100,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathArcCosH {})
}
}

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathArcSin;
impl Command for SubCommand {
impl Command for MathArcSin {
fn name(&self) -> &str {
"math arcsin"
}
@ -115,6 +115,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathArcSin {})
}
}

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathArcSinH;
impl Command for SubCommand {
impl Command for MathArcSinH {
fn name(&self) -> &str {
"math arcsinh"
}
@ -88,6 +88,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathArcSinH {})
}
}

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathArcTan;
impl Command for SubCommand {
impl Command for MathArcTan {
fn name(&self) -> &str {
"math arctan"
}
@ -102,6 +102,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathArcTan {})
}
}

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathArcTanH;
impl Command for SubCommand {
impl Command for MathArcTanH {
fn name(&self) -> &str {
"math arctanh"
}
@ -101,6 +101,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathArcTanH {})
}
}

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathCos;
impl Command for SubCommand {
impl Command for MathCos {
fn name(&self) -> &str {
"math cos"
}
@ -108,6 +108,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathCos {})
}
}

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathCosH;
impl Command for SubCommand {
impl Command for MathCosH {
fn name(&self) -> &str {
"math cosh"
}
@ -88,6 +88,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathCosH {})
}
}

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathExp;
impl Command for SubCommand {
impl Command for MathExp {
fn name(&self) -> &str {
"math exp"
}
@ -93,6 +93,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathExp {})
}
}

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathLn;
impl Command for SubCommand {
impl Command for MathLn {
fn name(&self) -> &str {
"math ln"
}
@ -100,6 +100,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathLn {})
}
}

View File

@ -15,19 +15,19 @@ mod arcsinh;
mod arctan;
mod arctanh;
pub use cos::SubCommand as MathCos;
pub use cosh::SubCommand as MathCosH;
pub use sin::SubCommand as MathSin;
pub use sinh::SubCommand as MathSinH;
pub use tan::SubCommand as MathTan;
pub use tanh::SubCommand as MathTanH;
pub use cos::MathCos;
pub use cosh::MathCosH;
pub use sin::MathSin;
pub use sinh::MathSinH;
pub use tan::MathTan;
pub use tanh::MathTanH;
pub use exp::SubCommand as MathExp;
pub use ln::SubCommand as MathLn;
pub use exp::MathExp;
pub use ln::MathLn;
pub use arccos::SubCommand as MathArcCos;
pub use arccosh::SubCommand as MathArcCosH;
pub use arcsin::SubCommand as MathArcSin;
pub use arcsinh::SubCommand as MathArcSinH;
pub use arctan::SubCommand as MathArcTan;
pub use arctanh::SubCommand as MathArcTanH;
pub use arccos::MathArcCos;
pub use arccosh::MathArcCosH;
pub use arcsin::MathArcSin;
pub use arcsinh::MathArcSinH;
pub use arctan::MathArcTan;
pub use arctanh::MathArcTanH;

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathSin;
impl Command for SubCommand {
impl Command for MathSin {
fn name(&self) -> &str {
"math sin"
}
@ -108,6 +108,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathSin {})
}
}

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathSinH;
impl Command for SubCommand {
impl Command for MathSinH {
fn name(&self) -> &str {
"math sinh"
}
@ -87,6 +87,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathSinH {})
}
}

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathTan;
impl Command for SubCommand {
impl Command for MathTan {
fn name(&self) -> &str {
"math tan"
}
@ -106,6 +106,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathTan {})
}
}

View File

@ -1,9 +1,9 @@
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathTanH;
impl Command for SubCommand {
impl Command for MathTanH {
fn name(&self) -> &str {
"math tanh"
}
@ -86,6 +86,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathTanH {})
}
}

View File

@ -1,14 +1,11 @@
mod bits;
mod conversions;
mod filters;
mod formats;
mod math;
mod platform;
mod strings;
pub use bits::{
Bits, BitsAnd, BitsInto, BitsNot, BitsOr, BitsRol, BitsRor, BitsShl, BitsShr, BitsXor,
};
pub use bits::{Bits, BitsAnd, BitsNot, BitsOr, BitsRol, BitsRor, BitsShl, BitsShr, BitsXor};
pub use formats::ToHtml;
pub use math::{MathArcCos, MathArcCosH, MathArcSin, MathArcSinH, MathArcTan, MathArcTanH};
pub use math::{MathCos, MathCosH, MathSin, MathSinH, MathTan, MathTanH};
@ -29,8 +26,6 @@ pub fn add_extra_command_context(mut engine_state: EngineState) -> EngineState {
};
}
bind_command!(conversions::Fmt);
bind_command!(
filters::UpdateCells,
filters::EachWhile,
@ -63,7 +58,6 @@ pub fn add_extra_command_context(mut engine_state: EngineState) -> EngineState {
bind_command! {
Bits,
BitsAnd,
BitsInto,
BitsNot,
BitsOr,
BitsRol,

View File

@ -111,8 +111,7 @@ impl Command for FormatBits {
}
}
// TODO: crate public only during deprecation
pub(crate) fn format_bits(
fn format_bits(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,

View File

@ -2,8 +2,6 @@ mod bits;
mod command;
mod number;
pub(crate) use bits::FormatBits;
pub(crate) use command::FormatPattern;
// TODO remove `format_bits` visibility after removal of into bits
pub(crate) use bits::{format_bits, FormatBits};
// TODO remove `format_number` visibility after removal of into bits
pub(crate) use number::{format_number, FormatNumber};
pub(crate) use number::FormatNumber;

View File

@ -3,9 +3,9 @@ use heck::ToLowerCamelCase;
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct StrCamelCase;
impl Command for SubCommand {
impl Command for StrCamelCase {
fn name(&self) -> &str {
"str camel-case"
}
@ -91,6 +91,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(StrCamelCase {})
}
}

View File

@ -3,9 +3,9 @@ use heck::ToKebabCase;
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct StrKebabCase;
impl Command for SubCommand {
impl Command for StrKebabCase {
fn name(&self) -> &str {
"str kebab-case"
}
@ -90,6 +90,6 @@ mod tests {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(StrKebabCase {})
}
}

View File

@ -6,13 +6,13 @@ mod snake_case;
mod str_;
mod title_case;
pub use camel_case::SubCommand as StrCamelCase;
pub use kebab_case::SubCommand as StrKebabCase;
pub use pascal_case::SubCommand as StrPascalCase;
pub use screaming_snake_case::SubCommand as StrScreamingSnakeCase;
pub use snake_case::SubCommand as StrSnakeCase;
pub use camel_case::StrCamelCase;
pub use kebab_case::StrKebabCase;
pub use pascal_case::StrPascalCase;
pub use screaming_snake_case::StrScreamingSnakeCase;
pub use snake_case::StrSnakeCase;
pub use str_::Str;
pub use title_case::SubCommand as StrTitleCase;
pub use title_case::StrTitleCase;
use nu_cmd_base::input_handler::{operate as general_operate, CmdArgument};
use nu_engine::command_prelude::*;

View File

@ -3,9 +3,9 @@ use heck::ToUpperCamelCase;
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct StrPascalCase;
impl Command for SubCommand {
impl Command for StrPascalCase {
fn name(&self) -> &str {
"str pascal-case"
}
@ -91,6 +91,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(StrPascalCase {})
}
}

View File

@ -3,9 +3,9 @@ use heck::ToShoutySnakeCase;
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct StrScreamingSnakeCase;
impl Command for SubCommand {
impl Command for StrScreamingSnakeCase {
fn name(&self) -> &str {
"str screaming-snake-case"
}
@ -91,6 +91,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(StrScreamingSnakeCase {})
}
}

View File

@ -3,9 +3,9 @@ use heck::ToSnakeCase;
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct StrSnakeCase;
impl Command for SubCommand {
impl Command for StrSnakeCase {
fn name(&self) -> &str {
"str snake-case"
}
@ -91,6 +91,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(StrSnakeCase {})
}
}

View File

@ -3,9 +3,9 @@ use heck::ToTitleCase;
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct StrTitleCase;
impl Command for SubCommand {
impl Command for StrTitleCase {
fn name(&self) -> &str {
"str title-case"
}
@ -86,6 +86,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(StrTitleCase {})
}
}

View File

@ -6,7 +6,7 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
edition = "2021"
license = "MIT"
name = "nu-cmd-lang"
version = "0.102.1"
version = "0.103.1"
[lib]
bench = false
@ -15,10 +15,10 @@ bench = false
workspace = true
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.102.1", default-features = false }
nu-parser = { path = "../nu-parser", version = "0.102.1" }
nu-protocol = { path = "../nu-protocol", version = "0.102.1", default-features = false }
nu-utils = { path = "../nu-utils", version = "0.102.1", default-features = false }
nu-engine = { path = "../nu-engine", version = "0.103.1", default-features = false }
nu-parser = { path = "../nu-parser", version = "0.103.1" }
nu-protocol = { path = "../nu-protocol", version = "0.103.1", default-features = false }
nu-utils = { path = "../nu-utils", version = "0.103.1", default-features = false }
itertools = { workspace = true }
shadow-rs = { version = "0.38", default-features = false }
@ -42,7 +42,6 @@ plugin = [
"os",
]
mimalloc = []
trash-support = []
sqlite = []
static-link-openssl = []

View File

@ -103,7 +103,7 @@ impl Command for Describe {
"category" => Value::test_string("default"),
)),
)),
"first_commit" => Value::test_string("date"),
"first_commit" => Value::test_string("datetime"),
"my_duration" => Value::test_string("duration"),
)),
))),

View File

@ -161,11 +161,7 @@ fn push_version_numbers(record: &mut Record, head: Span) {
}
fn global_allocator() -> &'static str {
if cfg!(feature = "mimalloc") {
"mimalloc"
} else {
"standard"
}
"standard"
}
fn features_enabled() -> Vec<String> {

View File

@ -5,7 +5,7 @@ edition = "2021"
license = "MIT"
name = "nu-cmd-plugin"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-plugin"
version = "0.102.1"
version = "0.103.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -13,10 +13,10 @@ version = "0.102.1"
workspace = true
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.102.1" }
nu-path = { path = "../nu-path", version = "0.102.1" }
nu-protocol = { path = "../nu-protocol", version = "0.102.1", features = ["plugin"] }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.102.1" }
nu-engine = { path = "../nu-engine", version = "0.103.1" }
nu-path = { path = "../nu-path", version = "0.103.1" }
nu-protocol = { path = "../nu-protocol", version = "0.103.1", features = ["plugin"] }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.103.1" }
itertools = { workspace = true }

View File

@ -5,7 +5,7 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-confi
edition = "2021"
license = "MIT"
name = "nu-color-config"
version = "0.102.1"
version = "0.103.1"
[lib]
bench = false
@ -14,12 +14,12 @@ bench = false
workspace = true
[dependencies]
nu-protocol = { path = "../nu-protocol", version = "0.102.1", default-features = false }
nu-engine = { path = "../nu-engine", version = "0.102.1", default-features = false }
nu-json = { path = "../nu-json", version = "0.102.1" }
nu-protocol = { path = "../nu-protocol", version = "0.103.1", default-features = false }
nu-engine = { path = "../nu-engine", version = "0.103.1", default-features = false }
nu-json = { path = "../nu-json", version = "0.103.1" }
nu-ansi-term = { workspace = true }
serde = { workspace = true, features = ["derive"] }
[dev-dependencies]
nu-test-support = { path = "../nu-test-support", version = "0.102.1" }
nu-test-support = { path = "../nu-test-support", version = "0.103.1" }

View File

@ -120,7 +120,7 @@ impl<'a> StyleComputer<'a> {
("int".to_string(), ComputableStyle::Static(Color::White.normal())),
("filesize".to_string(), ComputableStyle::Static(Color::Cyan.normal())),
("duration".to_string(), ComputableStyle::Static(Color::White.normal())),
("date".to_string(), ComputableStyle::Static(Color::Purple.normal())),
("datetime".to_string(), ComputableStyle::Static(Color::Purple.normal())),
("range".to_string(), ComputableStyle::Static(Color::White.normal())),
("float".to_string(), ComputableStyle::Static(Color::White.normal())),
("string".to_string(), ComputableStyle::Static(Color::White.normal())),

View File

@ -5,7 +5,7 @@ edition = "2021"
license = "MIT"
name = "nu-command"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
version = "0.102.1"
version = "0.103.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -16,21 +16,21 @@ bench = false
workspace = true
[dependencies]
nu-cmd-base = { path = "../nu-cmd-base", version = "0.102.1" }
nu-color-config = { path = "../nu-color-config", version = "0.102.1" }
nu-engine = { path = "../nu-engine", version = "0.102.1", default-features = false }
nu-glob = { path = "../nu-glob", version = "0.102.1" }
nu-json = { path = "../nu-json", version = "0.102.1" }
nu-parser = { path = "../nu-parser", version = "0.102.1" }
nu-path = { path = "../nu-path", version = "0.102.1" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.102.1" }
nu-protocol = { path = "../nu-protocol", version = "0.102.1", default-features = false }
nu-system = { path = "../nu-system", version = "0.102.1" }
nu-table = { path = "../nu-table", version = "0.102.1" }
nu-term-grid = { path = "../nu-term-grid", version = "0.102.1" }
nu-utils = { path = "../nu-utils", version = "0.102.1", default-features = false }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.103.1" }
nu-color-config = { path = "../nu-color-config", version = "0.103.1" }
nu-engine = { path = "../nu-engine", version = "0.103.1", default-features = false }
nu-glob = { path = "../nu-glob", version = "0.103.1" }
nu-json = { path = "../nu-json", version = "0.103.1" }
nu-parser = { path = "../nu-parser", version = "0.103.1" }
nu-path = { path = "../nu-path", version = "0.103.1" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.103.1" }
nu-protocol = { path = "../nu-protocol", version = "0.103.1", default-features = false }
nu-system = { path = "../nu-system", version = "0.103.1" }
nu-table = { path = "../nu-table", version = "0.103.1" }
nu-term-grid = { path = "../nu-term-grid", version = "0.103.1" }
nu-utils = { path = "../nu-utils", version = "0.103.1", default-features = false }
nu-ansi-term = { workspace = true }
nuon = { path = "../nuon", version = "0.102.1" }
nuon = { path = "../nuon", version = "0.103.1" }
alphanumeric-sort = { workspace = true }
base64 = { workspace = true }
@ -209,8 +209,8 @@ sqlite = ["rusqlite"]
trash-support = ["trash"]
[dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.102.1" }
nu-test-support = { path = "../nu-test-support", version = "0.102.1" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.103.1" }
nu-test-support = { path = "../nu-test-support", version = "0.103.1" }
dirs = { workspace = true }
mockito = { workspace = true, default-features = false }

View File

@ -13,9 +13,9 @@ impl CmdArgument for Arguments {
}
#[derive(Clone)]
pub struct SubCommand;
pub struct IntoBinary;
impl Command for SubCommand {
impl Command for IntoBinary {
fn name(&self) -> &str {
"into binary"
}
@ -204,7 +204,7 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(IntoBinary {})
}
#[rstest]

View File

@ -2,9 +2,9 @@ use nu_cmd_base::input_handler::{operate, CmdArgument};
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct IntoBool;
impl Command for SubCommand {
impl Command for IntoBool {
fn name(&self) -> &str {
"into bool"
}
@ -202,7 +202,7 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(IntoBool {})
}
#[test]

View File

@ -49,9 +49,9 @@ impl Zone {
}
#[derive(Clone)]
pub struct SubCommand;
pub struct IntoDatetime;
impl Command for SubCommand {
impl Command for IntoDatetime {
fn name(&self) -> &str {
"into datetime"
}
@ -408,13 +408,10 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
Err(reason) => {
match NaiveDateTime::parse_from_str(val, &dt.0) {
Ok(d) => {
let local_offset = *Local::now().offset();
let dt_fixed =
TimeZone::from_local_datetime(&local_offset, &d)
.single()
.unwrap_or_default();
Local.from_local_datetime(&d).single().unwrap_or_default();
Value::date (dt_fixed,head)
Value::date(dt_fixed.into(),head)
}
Err(_) => {
Value::error (
@ -498,14 +495,14 @@ fn list_human_readable_examples(span: Span) -> Value {
#[cfg(test)]
mod tests {
use super::*;
use super::{action, DatetimeFormat, SubCommand, Zone};
use super::{action, DatetimeFormat, IntoDatetime, Zone};
use nu_protocol::Type::Error;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(IntoDatetime {})
}
#[test]

View File

@ -4,9 +4,9 @@ use nu_protocol::{ast::Expr, Unit};
const NS_PER_SEC: i64 = 1_000_000_000;
#[derive(Clone)]
pub struct SubCommand;
pub struct IntoDuration;
impl Command for SubCommand {
impl Command for IntoDuration {
fn name(&self) -> &str {
"into duration"
}
@ -277,7 +277,7 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(IntoDuration {})
}
const NS_PER_SEC: i64 = 1_000_000_000;

View File

@ -4,9 +4,9 @@ use nu_engine::command_prelude::*;
use nu_utils::get_system_locale;
#[derive(Clone)]
pub struct SubCommand;
pub struct IntoFilesize;
impl Command for SubCommand {
impl Command for IntoFilesize {
fn name(&self) -> &str {
"into filesize"
}
@ -197,6 +197,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(IntoFilesize {})
}
}

View File

@ -2,9 +2,9 @@ use nu_cmd_base::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct IntoFloat;
impl Command for SubCommand {
impl Command for IntoFloat {
fn name(&self) -> &str {
"into float"
}
@ -134,7 +134,7 @@ mod tests {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(IntoFloat {})
}
#[test]

View File

@ -12,9 +12,9 @@ impl CmdArgument for Arguments {
}
#[derive(Clone)]
pub struct SubCommand;
pub struct IntoGlob;
impl Command for SubCommand {
impl Command for IntoGlob {
fn name(&self) -> &str {
"into glob"
}
@ -121,6 +121,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(IntoGlob {})
}
}

View File

@ -18,9 +18,9 @@ impl CmdArgument for Arguments {
}
#[derive(Clone)]
pub struct SubCommand;
pub struct IntoInt;
impl Command for SubCommand {
impl Command for IntoInt {
fn name(&self) -> &str {
"into int"
}
@ -521,7 +521,7 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(IntoInt {})
}
#[test]

View File

@ -12,16 +12,16 @@ mod record;
mod string;
mod value;
pub use self::bool::SubCommand as IntoBool;
pub use self::filesize::SubCommand as IntoFilesize;
pub use binary::SubCommand as IntoBinary;
pub use binary::IntoBinary;
pub use bool::IntoBool;
pub use cell_path::IntoCellPath;
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;
pub use datetime::IntoDatetime;
pub use duration::IntoDuration;
pub use filesize::IntoFilesize;
pub use float::IntoFloat;
pub use glob::IntoGlob;
pub use int::IntoInt;
pub use record::IntoRecord;
pub use string::IntoString;
pub use value::IntoValue;

View File

@ -3,9 +3,9 @@ use nu_engine::command_prelude::*;
use nu_protocol::format_duration_as_timeperiod;
#[derive(Clone)]
pub struct SubCommand;
pub struct IntoRecord;
impl Command for SubCommand {
impl Command for IntoRecord {
fn name(&self) -> &str {
"into record"
}
@ -243,6 +243,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(IntoRecord {})
}
}

View File

@ -1,6 +1,6 @@
use nu_cmd_base::input_handler::{operate, CmdArgument};
use nu_engine::command_prelude::*;
use nu_protocol::{shell_error::into_code, Config};
use nu_protocol::Config;
use nu_utils::get_system_locale;
use num_format::ToFormattedString;
use std::sync::Arc;
@ -19,9 +19,9 @@ impl CmdArgument for Arguments {
}
#[derive(Clone)]
pub struct SubCommand;
pub struct IntoString;
impl Command for SubCommand {
impl Command for IntoString {
fn name(&self) -> &str {
"into string"
}
@ -215,17 +215,8 @@ 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, .. } => {
if group_digits {
let number = val.parse::<i64>().unwrap_or_default();
let decimal_value = digits.unwrap_or(0) as usize;
Value::string(format_int(number, group_digits, decimal_value), span)
} else {
Value::string(val.to_string(), span)
}
}
Value::Glob { val, .. } => Value::string(val.to_string(), span),
Value::String { val, .. } => Value::string(val, span),
Value::Glob { val, .. } => Value::string(val, span),
Value::Filesize { val, .. } => {
if group_digits {
let decimal_value = digits.unwrap_or(0) as usize;
@ -235,8 +226,6 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
}
}
Value::Duration { val: _, .. } => Value::string(input.to_expanded_string("", config), span),
Value::Error { error, .. } => Value::string(into_code(error).unwrap_or_default(), span),
Value::Nothing { .. } => Value::string("".to_string(), span),
Value::Record { .. } => Value::error(
// Watch out for CantConvert's argument order
@ -272,6 +261,7 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
})
.unwrap_or_else(|err| Value::error(err, span))
}
Value::Error { .. } => input.clone(),
x => Value::error(
ShellError::CantConvert {
to_type: String::from("string"),
@ -316,6 +306,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(IntoString {})
}
}

View File

@ -208,7 +208,7 @@ fn process_cell(val: Value, display_as_filesizes: bool, span: Span) -> Result<Va
}
} else if DATETIME_DMY_RE.is_match(&val_str).unwrap_or(false) {
let dt = parse_date_from_string(&val_str, span).map_err(|_| ShellError::CantConvert {
to_type: "date".to_string(),
to_type: "datetime".to_string(),
from_type: "string".to_string(),
span,
help: Some(format!(
@ -219,7 +219,7 @@ fn process_cell(val: Value, display_as_filesizes: bool, span: Span) -> Result<Va
Ok(Value::date(dt, span))
} else if DATETIME_YMD_RE.is_match(&val_str).unwrap_or(false) {
let dt = parse_date_from_string(&val_str, span).map_err(|_| ShellError::CantConvert {
to_type: "date".to_string(),
to_type: "datetime".to_string(),
from_type: "string".to_string(),
span,
help: Some(format!(
@ -230,7 +230,7 @@ fn process_cell(val: Value, display_as_filesizes: bool, span: Span) -> Result<Va
Ok(Value::date(dt, span))
} else if DATETIME_YMDZ_RE.is_match(&val_str).unwrap_or(false) {
let dt = parse_date_from_string(&val_str, span).map_err(|_| ShellError::CantConvert {
to_type: "date".to_string(),
to_type: "datetime".to_string(),
from_type: "string".to_string(),
span,
help: Some(format!(

View File

@ -4,4 +4,4 @@ mod split_cell_path;
pub use fill::Fill;
pub use into::*;
pub use split_cell_path::SubCommand as SplitCellPath;
pub use split_cell_path::SplitCellPath;

View File

@ -2,9 +2,9 @@ use nu_engine::command_prelude::*;
use nu_protocol::{ast::PathMember, IntoValue};
#[derive(Clone)]
pub struct SubCommand;
pub struct SplitCellPath;
impl Command for SubCommand {
impl Command for SplitCellPath {
fn name(&self) -> &str {
"split cell-path"
}
@ -150,6 +150,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(SplitCellPath {})
}
}

View File

@ -4,9 +4,9 @@ use chrono_humanize::HumanTime;
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct DateHumanize;
impl Command for SubCommand {
impl Command for DateHumanize {
fn name(&self) -> &str {
"date humanize"
}
@ -101,6 +101,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(DateHumanize {})
}
}

View File

@ -2,9 +2,9 @@ use chrono_tz::TZ_VARIANTS;
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct DateListTimezones;
impl Command for SubCommand {
impl Command for DateListTimezones {
fn name(&self) -> &str {
"date list-timezone"
}

View File

@ -7,8 +7,8 @@ mod to_timezone;
mod utils;
pub use date_::Date;
pub use humanize::SubCommand as DateHumanize;
pub use list_timezone::SubCommand as DateListTimezones;
pub use now::SubCommand as DateNow;
pub use to_timezone::SubCommand as DateToTimezone;
pub use humanize::DateHumanize;
pub use list_timezone::DateListTimezones;
pub use now::DateNow;
pub use to_timezone::DateToTimezone;
pub(crate) use utils::{generate_strftime_list, parse_date_from_string};

View File

@ -2,9 +2,9 @@ use chrono::Local;
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct DateNow;
impl Command for SubCommand {
impl Command for DateNow {
fn name(&self) -> &str {
"date now"
}

View File

@ -4,9 +4,9 @@ use chrono::{DateTime, FixedOffset, Local, LocalResult, TimeZone};
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct DateToTimezone;
impl Command for SubCommand {
impl Command for DateToTimezone {
fn name(&self) -> &str {
"date to-timezone"
}
@ -147,6 +147,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(DateToTimezone {})
}
}

View File

@ -70,7 +70,6 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
ParEach,
ChunkBy,
Prepend,
Range,
Reduce,
Reject,
Rename,
@ -455,7 +454,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
Job,
};
#[cfg(unix)]
#[cfg(all(unix, feature = "os"))]
bind_command! {
JobUnfreeze,
}

View File

@ -72,7 +72,7 @@ fn convert_string_to_value(
Err(x) => match x {
nu_json::Error::Syntax(_, row, col) => {
let label = x.to_string();
let label_span = convert_row_column_to_span(row, col, string_input);
let label_span = Span::from_row_column(row, col, string_input);
Err(ShellError::GenericError {
error: "Error while parsing JSON text".into(),
msg: "error parsing JSON text".into(),
@ -173,23 +173,3 @@ fn expand_closure(
_ => None,
}
}
// Converts row+column to a Span, assuming bytes (1-based rows)
fn convert_row_column_to_span(row: usize, col: usize, contents: &str) -> Span {
let mut cur_row = 1;
let mut cur_col = 1;
for (offset, curr_byte) in contents.bytes().enumerate() {
if curr_byte == b'\n' {
cur_row += 1;
cur_col = 1;
}
if cur_row >= row && cur_col >= col {
return Span::new(offset, offset);
} else {
cur_col += 1;
}
}
Span::new(contents.len(), contents.len())
}

View File

@ -8,8 +8,8 @@ use std::{
use nu_engine::{command_prelude::*, ClosureEvalOnce};
use nu_protocol::{
engine::{Closure, Job, ThreadJob},
report_shell_error, Signals,
engine::{Closure, Job, Redirection, ThreadJob},
report_shell_error, OutDest, Signals,
};
#[derive(Clone)]
@ -76,15 +76,18 @@ impl Command for JobSpawn {
let result = thread::Builder::new()
.name(format!("background job {}", id.get()))
.spawn(move || {
ClosureEvalOnce::new(&job_state, &job_stack, closure)
let mut stack = job_stack.reset_pipes();
let stack = stack.push_redirection(
Some(Redirection::Pipe(OutDest::Null)),
Some(Redirection::Pipe(OutDest::Null)),
);
ClosureEvalOnce::new_preserve_out_dest(&job_state, &stack, closure)
.run_with_input(Value::nothing(head).into_pipeline_data())
.and_then(|data| data.into_value(head))
.and_then(|data| data.drain())
.unwrap_or_else(|err| {
if !job_state.signals().interrupted() {
report_shell_error(&job_state, &err);
}
Value::nothing(head)
});
{

View File

@ -4,7 +4,7 @@ mod job_kill;
mod job_list;
mod job_spawn;
#[cfg(unix)]
#[cfg(all(unix, feature = "os"))]
mod job_unfreeze;
pub use is_admin::IsAdmin;
@ -14,5 +14,5 @@ pub use job_list::JobList;
pub use job_spawn::JobSpawn;
#[cfg(unix)]
#[cfg(all(unix, feature = "os"))]
pub use job_unfreeze::JobUnfreeze;

View File

@ -802,7 +802,7 @@ mod windows_helper {
use std::os::windows::prelude::OsStrExt;
use windows::Win32::Foundation::FILETIME;
use windows::Win32::Storage::FileSystem::{
FindFirstFileW, FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_READONLY,
FindClose, FindFirstFileW, FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_READONLY,
FILE_ATTRIBUTE_REPARSE_POINT, WIN32_FIND_DATAW,
};
use windows::Win32::System::SystemServices::{
@ -925,7 +925,14 @@ mod windows_helper {
windows::core::PCWSTR(filename_wide.as_ptr()),
&mut find_data,
) {
Ok(_) => Ok(find_data),
Ok(handle) => {
// Don't forget to close the Find handle
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfilew#remarks
// Assumption: WIN32_FIND_DATAW is a pure data struct, so we can let our
// find_data outlive the handle.
let _ = FindClose(handle);
Ok(find_data)
}
Err(e) => Err(ShellError::Io(IoError::new_with_additional_context(
std::io::ErrorKind::Other,
span,

View File

@ -1,11 +1,15 @@
#[allow(deprecated)]
use nu_engine::{command_prelude::*, current_dir, get_eval_block};
use nu_engine::{command_prelude::*, current_dir, eval_call};
use nu_protocol::{
ast,
debugger::{WithDebug, WithoutDebug},
shell_error::{self, io::IoError},
DataSource, NuGlob, PipelineMetadata,
};
use std::path::{Path, PathBuf};
use std::{
collections::HashMap,
path::{Path, PathBuf},
};
#[cfg(feature = "sqlite")]
use crate::database::SQLiteDatabase;
@ -63,7 +67,6 @@ impl Command for Open {
#[allow(deprecated)]
let cwd = current_dir(engine_state, stack)?;
let mut paths = call.rest::<Spanned<NuGlob>>(engine_state, stack, 0)?;
let eval_block = get_eval_block(engine_state);
if paths.is_empty() && !call.has_positional_args(stack, 0) {
// try to use path from pipeline input if there were no positional or spread args
@ -192,13 +195,16 @@ impl Command for Open {
match converter {
Some((converter_id, ext)) => {
let decl = engine_state.get_decl(converter_id);
let command_output = if let Some(block_id) = decl.block_id() {
let block = engine_state.get_block(block_id);
eval_block(engine_state, stack, block, stream)
let open_call = ast::Call {
decl_id: converter_id,
head: call_span,
arguments: vec![],
parser_info: HashMap::new(),
};
let command_output = if engine_state.is_debugging() {
eval_call::<WithDebug>(engine_state, stack, &open_call, stream)
} else {
let call = ast::Call::new(call_span);
decl.run(engine_state, stack, &(&call).into(), stream)
eval_call::<WithoutDebug>(engine_state, stack, &open_call, stream)
};
output.push(command_output.map_err(|inner| {
ShellError::GenericError{

View File

@ -158,17 +158,15 @@ impl Command for UTouch {
continue;
}
let mut expanded_globs = glob(
&file_path.to_string_lossy(),
Some(engine_state.signals().clone()),
)
.unwrap_or_else(|_| {
panic!(
"Failed to process file path: {}",
&file_path.to_string_lossy()
)
})
.peekable();
let mut expanded_globs =
glob(&file_path.to_string_lossy(), engine_state.signals().clone())
.unwrap_or_else(|_| {
panic!(
"Failed to process file path: {}",
&file_path.to_string_lossy()
)
})
.peekable();
if expanded_globs.peek().is_none() {
let file_name = file_path.file_name().unwrap_or_else(|| {

View File

@ -23,6 +23,11 @@ impl Command for Default {
SyntaxShape::String,
"The name of the column.",
)
.switch(
"empty",
"also replace empty items like \"\", {}, and []",
Some('e'),
)
.category(Category::Filters)
}
@ -37,7 +42,8 @@ impl Command for Default {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
default(engine_state, stack, call, input)
let empty = call.has_flag(engine_state, stack, "empty")?;
default(engine_state, stack, call, input, empty)
}
fn examples(&self) -> Vec<Example> {
@ -80,6 +86,20 @@ impl Command for Default {
}),
])),
},
Example {
description: r#"Replace the empty string in the "a" column of a list"#,
example: "[{a:1 b:2} {a:'' b:1}] | default -e 'N/A' a",
result: Some(Value::test_list(vec![
Value::test_record(record! {
"a" => Value::test_int(1),
"b" => Value::test_int(2),
}),
Value::test_record(record! {
"a" => Value::test_string("N/A"),
"b" => Value::test_int(1),
}),
])),
},
]
}
}
@ -89,6 +109,7 @@ fn default(
stack: &mut Stack,
call: &Call,
input: PipelineData,
default_when_empty: bool,
) -> Result<PipelineData, ShellError> {
let metadata = input.metadata();
let value: Value = call.req(engine_state, stack, 0)?;
@ -104,7 +125,9 @@ fn default(
} => {
let record = record.to_mut();
if let Some(val) = record.get_mut(&column.item) {
if matches!(val, Value::Nothing { .. }) {
if matches!(val, Value::Nothing { .. })
|| (default_when_empty && val.is_empty())
{
*val = value.clone();
}
} else {
@ -118,7 +141,10 @@ fn default(
engine_state.signals(),
)
.map(|x| x.set_metadata(metadata))
} else if input.is_nothing() {
} else if input.is_nothing()
|| (default_when_empty
&& matches!(input, PipelineData::Value(ref value, _) if value.is_empty()))
{
Ok(value.into_pipeline_data())
} else {
Ok(input)

View File

@ -31,7 +31,6 @@ mod merge;
mod move_;
mod par_each;
mod prepend;
mod range;
mod reduce;
mod reject;
mod rename;
@ -91,7 +90,6 @@ pub use merge::MergeDeep;
pub use move_::Move;
pub use par_each::ParEach;
pub use prepend::Prepend;
pub use range::Range;
pub use reduce::Reduce;
pub use reject::Reject;
pub use rename::Rename;

View File

@ -1,90 +0,0 @@
use nu_engine::command_prelude::*;
use nu_protocol::{report_parse_warning, ParseWarning};
#[derive(Clone)]
pub struct Range;
impl Command for Range {
fn name(&self) -> &str {
"range"
}
fn signature(&self) -> Signature {
Signature::build("range")
.input_output_types(vec![(
Type::List(Box::new(Type::Any)),
Type::List(Box::new(Type::Any)),
)])
.required("rows", SyntaxShape::Range, "Range of rows to return.")
.category(Category::Deprecated)
}
fn description(&self) -> &str {
"Return only the selected rows."
}
fn search_terms(&self) -> Vec<&str> {
vec!["filter", "head", "tail", "slice"]
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
example: "[0,1,2,3,4,5] | range 4..5",
description: "Get the last 2 items",
result: Some(Value::list(
vec![Value::test_int(4), Value::test_int(5)],
Span::test_data(),
)),
},
Example {
example: "[0,1,2,3,4,5] | range (-2)..",
description: "Get the last 2 items",
result: Some(Value::list(
vec![Value::test_int(4), Value::test_int(5)],
Span::test_data(),
)),
},
Example {
example: "[0,1,2,3,4,5] | range (-3)..-2",
description: "Get the next to last 2 items",
result: Some(Value::list(
vec![Value::test_int(3), Value::test_int(4)],
Span::test_data(),
)),
},
]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
report_parse_warning(
&StateWorkingSet::new(engine_state),
&ParseWarning::DeprecatedWarning {
old_command: "range".into(),
new_suggestion: "use `slice`".into(),
span: head,
url: "`help slice`".into(),
},
);
super::Slice::run(&super::Slice, engine_state, stack, call, input)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Range {})
}
}

View File

@ -184,26 +184,6 @@ fn convert_nujson_to_value(value: nu_json::Value, span: Span) -> Value {
}
}
// Converts row+column to a Span, assuming bytes (1-based rows)
fn convert_row_column_to_span(row: usize, col: usize, contents: &str) -> Span {
let mut cur_row = 1;
let mut cur_col = 1;
for (offset, curr_byte) in contents.bytes().enumerate() {
if curr_byte == b'\n' {
cur_row += 1;
cur_col = 1;
}
if cur_row >= row && cur_col >= col {
return Span::new(offset, offset);
} else {
cur_col += 1;
}
}
Span::new(contents.len(), contents.len())
}
fn convert_string_to_value(string_input: &str, span: Span) -> Result<Value, ShellError> {
match nu_json::from_str(string_input) {
Ok(value) => Ok(convert_nujson_to_value(value, span)),
@ -211,7 +191,7 @@ fn convert_string_to_value(string_input: &str, span: Span) -> Result<Value, Shel
Err(x) => match x {
nu_json::Error::Syntax(_, row, col) => {
let label = x.to_string();
let label_span = convert_row_column_to_span(row, col, string_input);
let label_span = Span::from_row_column(row, col, string_input);
Err(ShellError::GenericError {
error: "Error while parsing JSON text".into(),
msg: "error parsing JSON text".into(),
@ -240,7 +220,7 @@ fn convert_string_to_value_strict(string_input: &str, span: Span) -> Result<Valu
Ok(value) => Ok(convert_nujson_to_value(value, span)),
Err(err) => Err(if err.is_syntax() {
let label = err.to_string();
let label_span = convert_row_column_to_span(err.line(), err.column(), string_input);
let label_span = Span::from_row_column(err.line(), err.column(), string_input);
ShellError::GenericError {
error: "Error while parsing JSON text".into(),
msg: "error parsing JSON text".into(),

View File

@ -2,7 +2,7 @@ use crate::formats::nu_xml_format::{COLUMN_ATTRS_NAME, COLUMN_CONTENT_NAME, COLU
use indexmap::IndexMap;
use nu_engine::command_prelude::*;
use roxmltree::NodeType;
use roxmltree::{NodeType, ParsingOptions, TextPos};
#[derive(Clone)]
pub struct FromXml;
@ -16,6 +16,11 @@ impl Command for FromXml {
Signature::build("from xml")
.input_output_types(vec![(Type::String, Type::record())])
.switch("keep-comments", "add comment nodes to result", None)
.switch(
"allow-dtd",
"allow parsing documents with DTDs (may result in exponential entity expansion)",
None,
)
.switch(
"keep-pi",
"add processing instruction nodes to result",
@ -50,10 +55,12 @@ string. This way content of every tag is always a table and is easier to parse"#
let head = call.head;
let keep_comments = call.has_flag(engine_state, stack, "keep-comments")?;
let keep_processing_instructions = call.has_flag(engine_state, stack, "keep-pi")?;
let allow_dtd = call.has_flag(engine_state, stack, "allow-dtd")?;
let info = ParsingInfo {
span: head,
keep_comments,
keep_processing_instructions,
allow_dtd,
};
from_xml(input, &info)
}
@ -90,6 +97,7 @@ struct ParsingInfo {
span: Span,
keep_comments: bool,
keep_processing_instructions: bool,
allow_dtd: bool,
}
fn from_attributes_to_value(attributes: &[roxmltree::Attribute], info: &ParsingInfo) -> Value {
@ -198,7 +206,12 @@ fn from_document_to_value(d: &roxmltree::Document, info: &ParsingInfo) -> Value
}
fn from_xml_string_to_value(s: &str, info: &ParsingInfo) -> Result<Value, roxmltree::Error> {
let parsed = roxmltree::Document::parse(s)?;
let options = ParsingOptions {
allow_dtd: info.allow_dtd,
..Default::default()
};
let parsed = roxmltree::Document::parse_with_options(s, options)?;
Ok(from_document_to_value(&parsed, info))
}
@ -209,116 +222,135 @@ fn from_xml(input: PipelineData, info: &ParsingInfo) -> Result<PipelineData, She
Ok(x) => {
Ok(x.into_pipeline_data_with_metadata(metadata.map(|md| md.with_content_type(None))))
}
Err(err) => Err(process_xml_parse_error(err, span)),
Err(err) => Err(process_xml_parse_error(concat_string, err, span)),
}
}
fn process_xml_parse_error(err: roxmltree::Error, span: Span) -> ShellError {
fn process_xml_parse_error(source: String, err: roxmltree::Error, span: Span) -> ShellError {
match err {
roxmltree::Error::InvalidXmlPrefixUri(_) => make_cant_convert_error(
roxmltree::Error::InvalidXmlPrefixUri(pos) => make_xml_error_spanned(
"The `xmlns:xml` attribute must have an <http://www.w3.org/XML/1998/namespace> URI.",
span,
source, pos,
),
roxmltree::Error::UnexpectedXmlUri(_) => make_cant_convert_error(
roxmltree::Error::UnexpectedXmlUri(pos) => make_xml_error_spanned(
"Only the xmlns:xml attribute can have the http://www.w3.org/XML/1998/namespace URI.",
span,
source, pos,
),
roxmltree::Error::UnexpectedXmlnsUri(_) => make_cant_convert_error(
roxmltree::Error::UnexpectedXmlnsUri(pos) => make_xml_error_spanned(
"The http://www.w3.org/2000/xmlns/ URI must not be declared.",
span,
source, pos,
),
roxmltree::Error::InvalidElementNamePrefix(_) => {
make_cant_convert_error("xmlns can't be used as an element prefix.", span)
roxmltree::Error::InvalidElementNamePrefix(pos) => {
make_xml_error_spanned("xmlns can't be used as an element prefix.", source, pos)
}
roxmltree::Error::DuplicatedNamespace(_, _) => {
make_cant_convert_error("A namespace was already defined on this element.", span)
roxmltree::Error::DuplicatedNamespace(namespace, pos) => {
make_xml_error_spanned(format!("Namespace {namespace} was already defined on this element."), source, pos)
}
roxmltree::Error::UnknownNamespace(prefix, _) => {
make_cant_convert_error(format!("Unknown prefix {}", prefix), span)
roxmltree::Error::UnknownNamespace(prefix, pos) => {
make_xml_error_spanned(format!("Unknown prefix {}", prefix), source, pos)
}
roxmltree::Error::UnexpectedCloseTag { .. } => {
make_cant_convert_error("Unexpected close tag", span)
roxmltree::Error::UnexpectedCloseTag(expected, actual, pos) => {
make_xml_error_spanned(format!("Unexpected close tag {actual}, expected {expected}"), source, pos)
}
roxmltree::Error::UnexpectedEntityCloseTag(_) => {
make_cant_convert_error("Entity value starts with a close tag.", span)
roxmltree::Error::UnexpectedEntityCloseTag(pos) => {
make_xml_error_spanned("Entity value starts with a close tag.", source, pos)
}
roxmltree::Error::UnknownEntityReference(_, _) => make_cant_convert_error(
"A reference to an entity that was not defined in the DTD.",
span,
roxmltree::Error::UnknownEntityReference(entity, pos) => make_xml_error_spanned(
format!("Reference to unknown entity {entity} (was not defined in the DTD)"),
source, pos,
),
roxmltree::Error::MalformedEntityReference(_) => {
make_cant_convert_error("A malformed entity reference.", span)
roxmltree::Error::MalformedEntityReference(pos) => {
make_xml_error_spanned("Malformed entity reference.", source, pos)
}
roxmltree::Error::EntityReferenceLoop(_) => {
make_cant_convert_error("A possible entity reference loop.", span)
roxmltree::Error::EntityReferenceLoop(pos) => {
make_xml_error_spanned("Possible entity reference loop.", source, pos)
}
roxmltree::Error::InvalidAttributeValue(_) => {
make_cant_convert_error("Attribute value cannot have a < character.", span)
roxmltree::Error::InvalidAttributeValue(pos) => {
make_xml_error_spanned("Attribute value cannot have a < character.", source, pos)
}
roxmltree::Error::DuplicatedAttribute(_, _) => {
make_cant_convert_error("An element has a duplicated attributes.", span)
roxmltree::Error::DuplicatedAttribute(attribute, pos) => {
make_xml_error_spanned(format!("Element has a duplicated attribute: {attribute}"), source, pos)
}
roxmltree::Error::NoRootNode => {
make_cant_convert_error("The XML document must have at least one element.", span)
make_xml_error("The XML document must have at least one element.", span)
}
roxmltree::Error::UnclosedRootNode => {
make_cant_convert_error("The root node was opened but never closed.", span)
make_xml_error("The root node was opened but never closed.", span)
}
roxmltree::Error::DtdDetected => make_cant_convert_error(
"An XML with DTD detected. DTDs are currently disabled due to security reasons.",
span,
roxmltree::Error::DtdDetected => make_xml_error(
"XML document with DTD detected.\nDTDs are disabled by default to prevent denial-of-service attacks (use --allow-dtd to parse anyway)",
span
),
roxmltree::Error::NodesLimitReached => {
make_cant_convert_error("Node limit was reached.", span)
make_xml_error("Node limit was reached.", span)
}
roxmltree::Error::AttributesLimitReached => {
make_cant_convert_error("Attribute limit reached", span)
make_xml_error("Attribute limit reached", span)
}
roxmltree::Error::NamespacesLimitReached => {
make_cant_convert_error("Namespace limit reached", span)
make_xml_error("Namespace limit reached", span)
}
roxmltree::Error::UnexpectedDeclaration(_) => {
make_cant_convert_error("An XML document can have only one XML declaration and it must be at the start of the document.", span)
roxmltree::Error::UnexpectedDeclaration(pos) => {
make_xml_error_spanned("An XML document can have only one XML declaration and it must be at the start of the document.", source, pos)
}
roxmltree::Error::InvalidName(_) => {
make_cant_convert_error("Invalid name found.", span)
roxmltree::Error::InvalidName(pos) => {
make_xml_error_spanned("Invalid name.", source, pos)
}
roxmltree::Error::NonXmlChar(_, _) => {
make_cant_convert_error("A non-XML character has occurred. Valid characters are: <https://www.w3.org/TR/xml/#char32>", span)
roxmltree::Error::NonXmlChar(_, pos) => {
make_xml_error_spanned("Non-XML character found. Valid characters are: <https://www.w3.org/TR/xml/#char32>", source, pos)
}
roxmltree::Error::InvalidChar(_, _, _) => {
make_cant_convert_error("An invalid/unexpected character in XML.", span)
roxmltree::Error::InvalidChar(expected, actual, pos) => {
make_xml_error_spanned(
format!("Unexpected character {}, expected {}", actual as char, expected as char),
source,
pos
)
}
roxmltree::Error::InvalidChar2(_, _, _) => {
make_cant_convert_error("An invalid/unexpected character in XML.", span)
roxmltree::Error::InvalidChar2(expected, actual, pos) => {
make_xml_error_spanned(
format!("Unexpected character {}, expected {}", actual as char, expected),
source,
pos
)
}
roxmltree::Error::InvalidString(_, _) => {
make_cant_convert_error("An invalid/unexpected string in XML.", span)
roxmltree::Error::InvalidString(_, pos) => {
make_xml_error_spanned("Invalid/unexpected string in XML.", source, pos)
}
roxmltree::Error::InvalidExternalID(_) => {
make_cant_convert_error("An invalid ExternalID in the DTD.", span)
roxmltree::Error::InvalidExternalID(pos) => {
make_xml_error_spanned("Invalid ExternalID in the DTD.", source, pos)
}
roxmltree::Error::InvalidComment(_) => {
make_cant_convert_error("A comment cannot contain `--` or end with `-`.", span)
roxmltree::Error::InvalidComment(pos) => {
make_xml_error_spanned("A comment cannot contain `--` or end with `-`.", source, pos)
}
roxmltree::Error::InvalidCharacterData(_) => {
make_cant_convert_error("A Character Data node contains an invalid data. Currently, only `]]>` is not allowed.", span)
roxmltree::Error::InvalidCharacterData(pos) => {
make_xml_error_spanned("Character Data node contains an invalid data. Currently, only `]]>` is not allowed.", source, pos)
}
roxmltree::Error::UnknownToken(_) => {
make_cant_convert_error("Unknown token in XML.", span)
roxmltree::Error::UnknownToken(pos) => {
make_xml_error_spanned("Unknown token in XML.", source, pos)
}
roxmltree::Error::UnexpectedEndOfStream => {
make_cant_convert_error("Unexpected end of stream while parsing XML.", span)
make_xml_error("Unexpected end of stream while parsing XML.", span)
}
}
}
fn make_cant_convert_error(help: impl Into<String>, span: Span) -> ShellError {
ShellError::CantConvert {
from_type: Type::String.to_string(),
to_type: "XML".to_string(),
fn make_xml_error(msg: impl Into<String>, span: Span) -> ShellError {
ShellError::GenericError {
error: "Failed to parse XML".into(),
msg: msg.into(),
help: None,
span: Some(span),
inner: vec![],
}
}
fn make_xml_error_spanned(msg: impl Into<String>, src: String, pos: TextPos) -> ShellError {
let span = Span::from_row_column(pos.row as usize, pos.col as usize, &src);
ShellError::OutsideSpannedLabeledError {
src,
error: "Failed to parse XML".into(),
msg: msg.into(),
span,
help: Some(help.into()),
}
}
@ -375,6 +407,7 @@ mod tests {
span: Span::test_data(),
keep_comments: false,
keep_processing_instructions: false,
allow_dtd: false,
};
from_xml_string_to_value(xml, &info)
}

View File

@ -57,7 +57,7 @@ impl Command for ToJson {
// allow ranges to expand and turn into array
let input = input.try_expand_range()?;
let value = input.into_value(span)?;
let json_value = value_to_json_value(engine_state, &value, serialize_types)?;
let json_value = value_to_json_value(engine_state, &value, span, serialize_types)?;
let json_result = if raw {
nu_json::to_string_raw(&json_value)
@ -78,16 +78,12 @@ impl Command for ToJson {
};
Ok(PipelineData::Value(res, Some(metadata)))
}
_ => Ok(Value::error(
ShellError::CantConvert {
to_type: "JSON".into(),
from_type: value.get_type().to_string(),
span,
help: None,
},
_ => Err(ShellError::CantConvert {
to_type: "JSON".into(),
from_type: value.get_type().to_string(),
span,
)
.into_pipeline_data()),
help: None,
}),
}
}
@ -118,6 +114,7 @@ impl Command for ToJson {
pub fn value_to_json_value(
engine_state: &EngineState,
v: &Value,
call_span: Span,
serialize_types: bool,
) -> Result<nu_json::Value, ShellError> {
let span = v.span();
@ -142,24 +139,20 @@ pub fn value_to_json_value(
),
Value::List { vals, .. } => {
nu_json::Value::Array(json_list(engine_state, vals, serialize_types)?)
nu_json::Value::Array(json_list(engine_state, vals, call_span, serialize_types)?)
}
Value::Error { error, .. } => return Err(*error.clone()),
Value::Closure { val, .. } => {
if serialize_types {
let block = engine_state.get_block(val.block_id);
if let Some(span) = block.span {
let contents_bytes = engine_state.get_span_contents(span);
let contents_string = String::from_utf8_lossy(contents_bytes);
nu_json::Value::String(contents_string.to_string())
} else {
nu_json::Value::String(format!(
"unable to retrieve block contents for json block_id {}",
val.block_id.get()
))
}
let closure_string = val.coerce_into_string(engine_state, span)?;
nu_json::Value::String(closure_string.to_string())
} else {
nu_json::Value::Null
return Err(ShellError::UnsupportedInput {
msg: "closures are currently not deserializable (use --serialize to serialize as a string)".into(),
input: "value originates from here".into(),
msg_span: call_span,
input_span: span,
});
}
}
Value::Range { .. } => nu_json::Value::Null,
@ -171,14 +164,14 @@ pub fn value_to_json_value(
for (k, v) in &**val {
m.insert(
k.clone(),
value_to_json_value(engine_state, v, serialize_types)?,
value_to_json_value(engine_state, v, call_span, serialize_types)?,
);
}
nu_json::Value::Object(m)
}
Value::Custom { val, .. } => {
let collected = val.to_base_value(span)?;
value_to_json_value(engine_state, &collected, serialize_types)?
value_to_json_value(engine_state, &collected, call_span, serialize_types)?
}
})
}
@ -186,12 +179,18 @@ pub fn value_to_json_value(
fn json_list(
engine_state: &EngineState,
input: &[Value],
call_span: Span,
serialize_types: bool,
) -> Result<Vec<nu_json::Value>, ShellError> {
let mut out = vec![];
for value in input {
out.push(value_to_json_value(engine_state, value, serialize_types)?);
out.push(value_to_json_value(
engine_state,
value,
call_span,
serialize_types,
)?);
}
Ok(out)

View File

@ -22,6 +22,11 @@ impl Command for ToMsgpack {
fn signature(&self) -> Signature {
Signature::build(self.name())
.input_output_type(Type::Any, Type::Binary)
.switch(
"serialize",
"serialize nushell types that cannot be deserialized",
Some('s'),
)
.category(Category::Formats)
}
@ -69,8 +74,8 @@ MessagePack: https://msgpack.org/
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
@ -83,7 +88,16 @@ MessagePack: https://msgpack.org/
let value = input.into_value(value_span)?;
let mut out = vec![];
write_value(&mut out, &value, 0)?;
let serialize_types = call.has_flag(engine_state, stack, "serialize")?;
write_value(
&mut out,
&value,
0,
engine_state,
call.head,
serialize_types,
)?;
Ok(Value::binary(out, call.head).into_pipeline_data_with_metadata(Some(metadata)))
}
@ -148,6 +162,9 @@ pub(crate) fn write_value(
out: &mut impl io::Write,
value: &Value,
depth: usize,
engine_state: &EngineState,
call_span: Span,
serialize_types: bool,
) -> Result<(), WriteError> {
use mp::ValueWriteError::InvalidMarkerWrite;
let span = value.span();
@ -196,6 +213,9 @@ pub(crate) fn write_value(
out,
&Value::list(val.into_range_iter(span, Signals::empty()).collect(), span),
depth,
engine_state,
call_span,
serialize_types,
)?;
}
Value::String { val, .. } => {
@ -208,13 +228,20 @@ pub(crate) fn write_value(
mp::write_map_len(out, convert(val.len(), span)?).err_span(span)?;
for (k, v) in val.iter() {
mp::write_str(out, k).err_span(span)?;
write_value(out, v, depth + 1)?;
write_value(out, v, depth + 1, engine_state, call_span, serialize_types)?;
}
}
Value::List { vals, .. } => {
mp::write_array_len(out, convert(vals.len(), span)?).err_span(span)?;
for val in vals {
write_value(out, val, depth + 1)?;
write_value(
out,
val,
depth + 1,
engine_state,
call_span,
serialize_types,
)?;
}
}
Value::Nothing { .. } => {
@ -222,11 +249,20 @@ pub(crate) fn write_value(
.map_err(InvalidMarkerWrite)
.err_span(span)?;
}
Value::Closure { .. } => {
// Closures can't be converted
mp::write_nil(out)
.map_err(InvalidMarkerWrite)
.err_span(span)?;
Value::Closure { val, .. } => {
if serialize_types {
let closure_string = val
.coerce_into_string(engine_state, span)
.map_err(|err| WriteError::Shell(Box::new(err)))?;
mp::write_str(out, &closure_string).err_span(span)?;
} else {
return Err(WriteError::Shell(Box::new(ShellError::UnsupportedInput {
msg: "closures are currently not deserializable (use --serialize to serialize as a string)".into(),
input: "value originates from here".into(),
msg_span: call_span,
input_span: span,
})));
}
}
Value::Error { error, .. } => {
return Err(WriteError::Shell(error.clone()));
@ -249,7 +285,14 @@ pub(crate) fn write_value(
mp::write_bin(out, val).err_span(span)?;
}
Value::Custom { val, .. } => {
write_value(out, &val.to_base_value(span)?, depth)?;
write_value(
out,
&val.to_base_value(span)?,
depth,
engine_state,
call_span,
serialize_types,
)?;
}
}
Ok(())

View File

@ -32,6 +32,11 @@ impl Command for ToMsgpackz {
"Window size for brotli compression (default 20)",
Some('w'),
)
.switch(
"serialize",
"serialize nushell types that cannot be deserialized",
Some('s'),
)
.category(Category::Formats)
}
@ -69,6 +74,7 @@ impl Command for ToMsgpackz {
.get_flag(engine_state, stack, "window-size")?
.map(to_u32)
.transpose()?;
let serialize_types = call.has_flag(engine_state, stack, "serialize")?;
let value_span = input.span().unwrap_or(call.head);
let value = input.into_value(value_span)?;
@ -80,7 +86,14 @@ impl Command for ToMsgpackz {
window_size.map(|w| w.item).unwrap_or(DEFAULT_WINDOW_SIZE),
);
write_value(&mut out, &value, 0)?;
write_value(
&mut out,
&value,
0,
engine_state,
call.head,
serialize_types,
)?;
out.flush()
.map_err(|err| IoError::new(err.kind(), call.head, None))?;
drop(out);

View File

@ -69,16 +69,9 @@ impl Command for ToNuon {
match nuon::to_nuon(engine_state, &value, style, Some(span), serialize_types) {
Ok(serde_nuon_string) => Ok(Value::string(serde_nuon_string, span)
.into_pipeline_data_with_metadata(Some(metadata))),
_ => Ok(Value::error(
ShellError::CantConvert {
to_type: "NUON".into(),
from_type: value.get_type().to_string(),
span,
help: None,
},
span,
)
.into_pipeline_data_with_metadata(Some(metadata))),
Err(error) => {
Ok(Value::error(error, span).into_pipeline_data_with_metadata(Some(metadata)))
}
}
}

View File

@ -1,9 +1,11 @@
use crate::math::utils::ensure_bounded;
use nu_engine::command_prelude::*;
use nu_protocol::Range;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathAbs;
impl Command for SubCommand {
impl Command for MathAbs {
fn name(&self) -> &str {
"math abs"
}
@ -21,6 +23,7 @@ impl Command for SubCommand {
Type::List(Box::new(Type::Duration)),
Type::List(Box::new(Type::Duration)),
),
(Type::Range, Type::List(Box::new(Type::Number))),
])
.allow_variants_without_examples(true)
.category(Category::Math)
@ -46,6 +49,19 @@ impl Command for SubCommand {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
if let PipelineData::Value(
Value::Range {
ref val,
internal_span,
},
..,
) = input
{
match &**val {
Range::IntRange(range) => ensure_bounded(range.end(), internal_span, head)?,
Range::FloatRange(range) => ensure_bounded(range.end(), internal_span, head)?,
}
}
input.map(move |value| abs_helper(value, head), engine_state.signals())
}
@ -56,6 +72,19 @@ impl Command for SubCommand {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
if let PipelineData::Value(
Value::Range {
ref val,
internal_span,
},
..,
) = input
{
match &**val {
Range::IntRange(range) => ensure_bounded(range.end(), internal_span, head)?,
Range::FloatRange(range) => ensure_bounded(range.end(), internal_span, head)?,
}
}
input.map(
move |value| abs_helper(value, head),
working_set.permanent().signals(),
@ -105,6 +134,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathAbs {})
}
}

View File

@ -6,9 +6,9 @@ use nu_engine::command_prelude::*;
const NS_PER_SEC: i64 = 1_000_000_000;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathAvg;
impl Command for SubCommand {
impl Command for MathAvg {
fn name(&self) -> &str {
"math avg"
}
@ -104,6 +104,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathAvg {})
}
}

View File

@ -1,9 +1,11 @@
use crate::math::utils::ensure_bounded;
use nu_engine::command_prelude::*;
use nu_protocol::Range;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathCeil;
impl Command for SubCommand {
impl Command for MathCeil {
fn name(&self) -> &str {
"math ceil"
}
@ -16,6 +18,7 @@ impl Command for SubCommand {
Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Int)),
),
(Type::Range, Type::List(Box::new(Type::Number))),
])
.allow_variants_without_examples(true)
.category(Category::Math)
@ -45,6 +48,19 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
if let PipelineData::Value(
Value::Range {
ref val,
internal_span,
},
..,
) = input
{
match &**val {
Range::IntRange(range) => ensure_bounded(range.end(), internal_span, head)?,
Range::FloatRange(range) => ensure_bounded(range.end(), internal_span, head)?,
}
}
input.map(move |value| operate(value, head), engine_state.signals())
}
@ -59,6 +75,19 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
if let PipelineData::Value(
Value::Range {
ref val,
internal_span,
},
..,
) = input
{
match &**val {
Range::IntRange(range) => ensure_bounded(range.end(), internal_span, head)?,
Range::FloatRange(range) => ensure_bounded(range.end(), internal_span, head)?,
}
}
input.map(
move |value| operate(value, head),
working_set.permanent().signals(),
@ -103,6 +132,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathCeil {})
}
}

View File

@ -1,9 +1,11 @@
use crate::math::utils::ensure_bounded;
use nu_engine::command_prelude::*;
use nu_protocol::Range;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathFloor;
impl Command for SubCommand {
impl Command for MathFloor {
fn name(&self) -> &str {
"math floor"
}
@ -16,6 +18,7 @@ impl Command for SubCommand {
Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Int)),
),
(Type::Range, Type::List(Box::new(Type::Number))),
])
.allow_variants_without_examples(true)
.category(Category::Math)
@ -45,6 +48,19 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
if let PipelineData::Value(
Value::Range {
ref val,
internal_span,
},
..,
) = input
{
match &**val {
Range::IntRange(range) => ensure_bounded(range.end(), internal_span, head)?,
Range::FloatRange(range) => ensure_bounded(range.end(), internal_span, head)?,
}
}
input.map(move |value| operate(value, head), engine_state.signals())
}
@ -59,6 +75,19 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
if let PipelineData::Value(
Value::Range {
ref val,
internal_span,
},
..,
) = input
{
match &**val {
Range::IntRange(range) => ensure_bounded(range.end(), internal_span, head)?,
Range::FloatRange(range) => ensure_bounded(range.end(), internal_span, head)?,
}
}
input.map(
move |value| operate(value, head),
working_set.permanent().signals(),
@ -103,6 +132,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathFloor {})
}
}

View File

@ -1,10 +1,12 @@
use crate::math::utils::ensure_bounded;
use nu_engine::command_prelude::*;
use nu_protocol::Range;
use nu_protocol::Signals;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathLog;
impl Command for SubCommand {
impl Command for MathLog {
fn name(&self) -> &str {
"math log"
}
@ -22,6 +24,7 @@ impl Command for SubCommand {
Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Float)),
),
(Type::Range, Type::List(Box::new(Type::Number))),
])
.allow_variants_without_examples(true)
.category(Category::Math)
@ -46,7 +49,21 @@ impl Command for SubCommand {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let base: Spanned<f64> = call.req(engine_state, stack, 0)?;
if let PipelineData::Value(
Value::Range {
ref val,
internal_span,
},
..,
) = input
{
match &**val {
Range::IntRange(range) => ensure_bounded(range.end(), internal_span, head)?,
Range::FloatRange(range) => ensure_bounded(range.end(), internal_span, head)?,
}
}
log(base, call.head, input, engine_state.signals())
}
@ -56,7 +73,21 @@ impl Command for SubCommand {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let base: Spanned<f64> = call.req_const(working_set, 0)?;
if let PipelineData::Value(
Value::Range {
ref val,
internal_span,
},
..,
) = input
{
match &**val {
Range::IntRange(range) => ensure_bounded(range.end(), internal_span, head)?,
Range::FloatRange(range) => ensure_bounded(range.end(), internal_span, head)?,
}
}
log(base, call.head, input, working_set.permanent().signals())
}
@ -159,6 +190,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathLog {})
}
}

View File

@ -5,9 +5,9 @@ use crate::math::{
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathMax;
impl Command for SubCommand {
impl Command for MathMax {
fn name(&self) -> &str {
"math max"
}
@ -97,6 +97,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathMax {})
}
}

View File

@ -3,9 +3,9 @@ use nu_engine::command_prelude::*;
use std::cmp::Ordering;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathMedian;
impl Command for SubCommand {
impl Command for MathMedian {
fn name(&self) -> &str {
"math median"
}
@ -151,6 +151,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathMedian {})
}
}

View File

@ -5,9 +5,9 @@ use crate::math::{
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathMin;
impl Command for SubCommand {
impl Command for MathMin {
fn name(&self) -> &str {
"math min"
}
@ -95,6 +95,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathMin {})
}
}

View File

@ -17,20 +17,20 @@ mod sum;
mod utils;
mod variance;
pub use abs::SubCommand as MathAbs;
pub use avg::SubCommand as MathAvg;
pub use ceil::SubCommand as MathCeil;
pub use floor::SubCommand as MathFloor;
pub use abs::MathAbs;
pub use avg::MathAvg;
pub use ceil::MathCeil;
pub use floor::MathFloor;
pub use math_::MathCommand as Math;
pub use max::SubCommand as MathMax;
pub use median::SubCommand as MathMedian;
pub use min::SubCommand as MathMin;
pub use mode::SubCommand as MathMode;
pub use product::SubCommand as MathProduct;
pub use round::SubCommand as MathRound;
pub use sqrt::SubCommand as MathSqrt;
pub use stddev::SubCommand as MathStddev;
pub use sum::SubCommand as MathSum;
pub use variance::SubCommand as MathVariance;
pub use max::MathMax;
pub use median::MathMedian;
pub use min::MathMin;
pub use mode::MathMode;
pub use product::MathProduct;
pub use round::MathRound;
pub use sqrt::MathSqrt;
pub use stddev::MathStddev;
pub use sum::MathSum;
pub use variance::MathVariance;
pub use self::log::SubCommand as MathLog;
pub use log::MathLog;

View File

@ -3,7 +3,7 @@ use nu_engine::command_prelude::*;
use std::{cmp::Ordering, collections::HashMap};
#[derive(Clone)]
pub struct SubCommand;
pub struct MathMode;
#[derive(Hash, Eq, PartialEq, Debug)]
enum NumberTypes {
@ -28,7 +28,7 @@ impl HashableType {
}
}
impl Command for SubCommand {
impl Command for MathMode {
fn name(&self) -> &str {
"math mode"
}
@ -110,7 +110,7 @@ impl Command for SubCommand {
}
}
pub fn mode(values: &[Value], _span: Span, head: Span) -> Result<Value, ShellError> {
pub fn mode(values: &[Value], span: Span, head: Span) -> Result<Value, ShellError> {
//In e-q, Value doesn't implement Hash or Eq, so we have to get the values inside
// But f64 doesn't implement Hash, so we get the binary representation to use as
// key in the HashMap
@ -130,11 +130,11 @@ pub fn mode(values: &[Value], _span: Span, head: Span) -> Result<Value, ShellErr
NumberTypes::Filesize,
)),
Value::Error { error, .. } => Err(*error.clone()),
other => Err(ShellError::UnsupportedInput {
_ => Err(ShellError::UnsupportedInput {
msg: "Unable to give a result with this input".to_string(),
input: "value originates from here".into(),
msg_span: head,
input_span: other.span(),
input_span: span,
}),
})
.collect::<Result<Vec<HashableType>, ShellError>>()?;
@ -183,6 +183,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathMode {})
}
}

View File

@ -5,9 +5,9 @@ use crate::math::{
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathProduct;
impl Command for SubCommand {
impl Command for MathProduct {
fn name(&self) -> &str {
"math product"
}
@ -88,6 +88,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathProduct {})
}
}

View File

@ -1,9 +1,11 @@
use crate::math::utils::ensure_bounded;
use nu_engine::command_prelude::*;
use nu_protocol::Range;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathRound;
impl Command for SubCommand {
impl Command for MathRound {
fn name(&self) -> &str {
"math round"
}
@ -16,6 +18,7 @@ impl Command for SubCommand {
Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Number)),
),
(Type::Range, Type::List(Box::new(Type::Number))),
])
.allow_variants_without_examples(true)
.named(
@ -52,6 +55,19 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
if let PipelineData::Value(
Value::Range {
ref val,
internal_span,
},
..,
) = input
{
match &**val {
Range::IntRange(range) => ensure_bounded(range.end(), internal_span, head)?,
Range::FloatRange(range) => ensure_bounded(range.end(), internal_span, head)?,
}
}
input.map(
move |value| operate(value, head, precision_param),
engine_state.signals(),
@ -70,6 +86,19 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
if let PipelineData::Value(
Value::Range {
ref val,
internal_span,
},
..,
) = input
{
match &**val {
Range::IntRange(range) => ensure_bounded(range.end(), internal_span, head)?,
Range::FloatRange(range) => ensure_bounded(range.end(), internal_span, head)?,
}
}
input.map(
move |value| operate(value, head, precision_param),
working_set.permanent().signals(),
@ -153,6 +182,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathRound {})
}
}

View File

@ -1,9 +1,11 @@
use crate::math::utils::ensure_bounded;
use nu_engine::command_prelude::*;
use nu_protocol::Range;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathSqrt;
impl Command for SubCommand {
impl Command for MathSqrt {
fn name(&self) -> &str {
"math sqrt"
}
@ -16,6 +18,7 @@ impl Command for SubCommand {
Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Float)),
),
(Type::Range, Type::List(Box::new(Type::Number))),
])
.allow_variants_without_examples(true)
.category(Category::Math)
@ -45,6 +48,19 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
if let PipelineData::Value(
Value::Range {
ref val,
internal_span,
},
..,
) = input
{
match &**val {
Range::IntRange(range) => ensure_bounded(range.end(), internal_span, head)?,
Range::FloatRange(range) => ensure_bounded(range.end(), internal_span, head)?,
}
}
input.map(move |value| operate(value, head), engine_state.signals())
}
@ -59,6 +75,19 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
if let PipelineData::Value(
Value::Range {
ref val,
internal_span,
},
..,
) = input
{
match &**val {
Range::IntRange(range) => ensure_bounded(range.end(), internal_span, head)?,
Range::FloatRange(range) => ensure_bounded(range.end(), internal_span, head)?,
}
}
input.map(
move |value| operate(value, head),
working_set.permanent().signals(),
@ -127,6 +156,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathSqrt {})
}
}

View File

@ -3,9 +3,9 @@ use crate::math::utils::run_with_function;
use nu_engine::command_prelude::*;
#[derive(Clone)]
pub struct SubCommand;
pub struct MathStddev;
impl Command for SubCommand {
impl Command for MathStddev {
fn name(&self) -> &str {
"math stddev"
}
@ -14,6 +14,7 @@ impl Command for SubCommand {
Signature::build("math stddev")
.input_output_types(vec![
(Type::List(Box::new(Type::Number)), Type::Number),
(Type::Range, Type::Number),
(Type::table(), Type::record()),
(Type::record(), Type::record()),
])
@ -53,6 +54,18 @@ impl Command for SubCommand {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let sample = call.has_flag(engine_state, stack, "sample")?;
let name = call.head;
let span = input.span().unwrap_or(name);
let input: PipelineData = match input.try_expand_range() {
Err(_) => {
return Err(ShellError::IncorrectValue {
msg: "Range must be bounded".to_string(),
val_span: span,
call_span: name,
});
}
Ok(val) => val,
};
run_with_function(call, input, compute_stddev(sample))
}
@ -63,6 +76,18 @@ impl Command for SubCommand {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let sample = call.has_flag_const(working_set, "sample")?;
let name = call.head;
let span = input.span().unwrap_or(name);
let input: PipelineData = match input.try_expand_range() {
Err(_) => {
return Err(ShellError::IncorrectValue {
msg: "Range must be bounded".to_string(),
val_span: span,
call_span: name,
});
}
Ok(val) => val,
};
run_with_function(call, input, compute_stddev(sample))
}
@ -111,6 +136,6 @@ mod test {
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
test_examples(MathStddev {})
}
}

Some files were not shown because too many files have changed in this diff Show More