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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -65,10 +65,11 @@ fn complete_rec(
for entry in result.filter_map(|e| e.ok()) { for entry in result.filter_map(|e| e.ok()) {
let entry_name = entry.file_name().to_string_lossy().into_owned(); 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(); let mut built = built.clone();
built.parts.push(entry_name.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 { if !want_directory || entry_isdir {
matcher.add(entry_name.clone(), (entry_name, built)); 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 let ls_colors = (engine_state.config.completions.use_ls_colors
&& engine_state.config.use_ansi_coloring.get(engine_state)) && engine_state.config.use_ansi_coloring.get(engine_state))
.then(|| { .then(|| {
let ls_colors_env_str = match stack.get_env_var(engine_state, "LS_COLORS") { let ls_colors_env_str = stack
Some(v) => env_to_string("LS_COLORS", v, engine_state, stack).ok(), .get_env_var(engine_state, "LS_COLORS")
None => None, .and_then(|v| env_to_string("LS_COLORS", v, engine_state, stack).ok());
};
get_ls_colors(ls_colors_env_str) get_ls_colors(ls_colors_env_str)
}); });
@ -263,19 +263,16 @@ pub fn complete_item(
} }
let is_dir = p.isdir; let is_dir = p.isdir;
let path = original_cwd.apply(p, path_separator); 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| { let style = ls_colors.as_ref().map(|lsc| {
lsc.style_for_path_with_metadata( lsc.style_for_path_with_metadata(&real_path, metadata.as_ref())
&path, .map(lscolors::Style::to_nu_ansi_term_style)
std::fs::symlink_metadata(expand_to_real_path(&path)) .unwrap_or_default()
.ok()
.as_ref(),
)
.map(lscolors::Style::to_nu_ansi_term_style)
.unwrap_or_default()
}); });
FileSuggestion { FileSuggestion {
span, span,
path: escape_path(path, want_directory), path: escape_path(path),
style, style,
is_dir, is_dir,
} }
@ -284,30 +281,30 @@ pub fn complete_item(
} }
// Fix files or folders with quotes or hashes // 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. // 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 pathbuf = nu_path::expand_tilde(path);
let path = pathbuf.to_string_lossy(); let path = pathbuf.to_string_lossy();
return if path.contains('\'') { if path.contains('\'') {
// decide to use double quote, also need to escape `"` in path // decide to use double quotes
// or else users can't do anything with completed path either. // Path as Debug will do the escaping for `"`, `\`
format!("\"{}\"", path.replace('"', r#"\""#)) format!("{:?}", path)
} else { } else {
format!("'{path}'") 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 { } 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::{ use nu_protocol::{
ast::{Argument, Call, Expr, Expression}, ast::{Argument, Call, Expr, Expression},
debugger::WithoutDebug, debugger::WithoutDebug,
engine::{Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
DeclId, PipelineData, Span, Type, Value, DeclId, PipelineData, Span, Type, Value,
}; };
use std::collections::HashMap; use std::collections::HashMap;
@ -42,28 +42,37 @@ impl<T: Completer> Completer for CustomCompletion<T> {
) -> Vec<SemanticSuggestion> { ) -> Vec<SemanticSuggestion> {
// Call custom declaration // Call custom declaration
let mut stack_mut = stack.clone(); let mut stack_mut = stack.clone();
let result = eval_call::<WithoutDebug>( let mut eval = |engine_state: &EngineState| {
working_set.permanent_state, eval_call::<WithoutDebug>(
&mut stack_mut, engine_state,
&Call { &mut stack_mut,
decl_id: self.decl_id, &Call {
head: span, decl_id: self.decl_id,
arguments: vec![ head: span,
Argument::Positional(Expression::new_unknown( arguments: vec![
Expr::String(self.line.clone()), Argument::Positional(Expression::new_unknown(
Span::unknown(), Expr::String(self.line.clone()),
Type::String, Span::unknown(),
)), Type::String,
Argument::Positional(Expression::new_unknown( )),
Expr::Int(self.line_pos as i64), Argument::Positional(Expression::new_unknown(
Span::unknown(), Expr::Int(self.line_pos as i64),
Type::Int, Span::unknown(),
)), Type::Int,
], )),
parser_info: HashMap::new(), ],
}, parser_info: HashMap::new(),
PipelineData::empty(), },
); 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 completion_options = orig_options.clone();
let mut should_sort = true; 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> { fn collection_comparison_ops() -> Vec<OperatorItem> {
let mut ops = common_comparison_ops(); let mut ops = common_comparison_ops();
ops.push(operator_to_item(Comparison::Has)); ops.push(operator_to_item(Comparison::Has));
@ -72,6 +81,10 @@ fn bit_ops() -> Vec<OperatorItem> {
ast::Bits::iter().map(operator_to_item).collect() 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> { fn numeric_assignment_ops() -> Vec<OperatorItem> {
ast::Assignment::iter() ast::Assignment::iter()
.filter(|op| !matches!(op, ast::Assignment::ConcatenateAssign)) .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::Filesize { .. } | Value::Duration { .. } => valid_value_with_unit_ops(),
Value::Range { .. } | Value::Record { .. } => collection_comparison_ops(), Value::Range { .. } | Value::Record { .. } => collection_comparison_ops(),
Value::List { .. } => valid_list_ops(), Value::List { .. } => valid_list_ops(),
_ => common_comparison_ops(), _ => all_ops_for_immutable(),
}; };
if mutable { if mutable {
ops.extend(match value { ops.extend(match value {
@ -165,7 +178,11 @@ fn ops_by_value(value: &Value, mutable: bool) -> Vec<OperatorItem> {
Value::String { .. } | Value::Binary { .. } | Value::List { .. } => { Value::String { .. } | Value::Binary { .. } | Value::List { .. } => {
concat_assignment_ops() 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 ops
@ -223,7 +240,7 @@ impl Completer for OperatorCompletion<'_> {
needs_assignment_ops = false; needs_assignment_ops = false;
ops_by_value(&value, mutable) ops_by_value(&value, mutable)
} }
_ => common_comparison_ops(), _ => all_ops_for_immutable(),
}, },
_ => common_comparison_ops(), _ => common_comparison_ops(),
}; };
@ -233,6 +250,7 @@ impl Completer for OperatorCompletion<'_> {
Type::Int | Type::Float | Type::Number => numeric_assignment_ops(), Type::Int | Type::Float | Type::Number => numeric_assignment_ops(),
Type::Filesize | Type::Duration => numeric_assignment_ops(), Type::Filesize | Type::Duration => numeric_assignment_ops(),
Type::String | Type::Binary | Type::List(_) => concat_assignment_ops(), Type::String | Type::Binary | Type::List(_) => concat_assignment_ops(),
Type::Any => all_assignment_ops(),
_ => vec![operator_to_item(ast::Assignment::Assign)], _ => vec![operator_to_item(ast::Assignment::Assign)],
}); });
} }

View File

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

View File

@ -5,7 +5,7 @@ edition = "2021"
license = "MIT" license = "MIT"
name = "nu-cmd-base" name = "nu-cmd-base"
repository = "https://github.com/nushell/nushell/tree/main/crates/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 # 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 workspace = true
[dependencies] [dependencies]
nu-engine = { path = "../nu-engine", 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.102.1" } nu-parser = { path = "../nu-parser", version = "0.103.1" }
nu-path = { path = "../nu-path", version = "0.102.1" } nu-path = { path = "../nu-path", version = "0.103.1" }
nu-protocol = { path = "../nu-protocol", version = "0.102.1", default-features = false } nu-protocol = { path = "../nu-protocol", version = "0.103.1", default-features = false }
indexmap = { workspace = true } indexmap = { workspace = true }
miette = { workspace = true } miette = { workspace = true }

View File

@ -5,7 +5,7 @@ edition = "2021"
license = "MIT" license = "MIT"
name = "nu-cmd-extra" name = "nu-cmd-extra"
repository = "https://github.com/nushell/nushell/tree/main/crates/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 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -16,13 +16,13 @@ bench = false
workspace = true workspace = true
[dependencies] [dependencies]
nu-cmd-base = { path = "../nu-cmd-base", version = "0.102.1" } nu-cmd-base = { path = "../nu-cmd-base", version = "0.103.1" }
nu-engine = { path = "../nu-engine", version = "0.102.1", default-features = false } nu-engine = { path = "../nu-engine", version = "0.103.1", default-features = false }
nu-json = { version = "0.102.1", path = "../nu-json" } nu-json = { version = "0.103.1", path = "../nu-json" }
nu-parser = { path = "../nu-parser", version = "0.102.1" } nu-parser = { path = "../nu-parser", version = "0.103.1" }
nu-pretty-hex = { version = "0.102.1", path = "../nu-pretty-hex" } nu-pretty-hex = { version = "0.103.1", path = "../nu-pretty-hex" }
nu-protocol = { path = "../nu-protocol", version = "0.102.1", default-features = false } nu-protocol = { path = "../nu-protocol", version = "0.103.1", default-features = false }
nu-utils = { path = "../nu-utils", version = "0.102.1", default-features = false } nu-utils = { path = "../nu-utils", version = "0.103.1", default-features = false }
# Potential dependencies for extras # Potential dependencies for extras
heck = { workspace = true } heck = { workspace = true }
@ -37,6 +37,6 @@ itertools = { workspace = true }
mime = { workspace = true } mime = { workspace = true }
[dev-dependencies] [dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.102.1" } nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.103.1" }
nu-command = { path = "../nu-command", version = "0.102.1" } nu-command = { path = "../nu-command", version = "0.103.1" }
nu-test-support = { path = "../nu-test-support", version = "0.102.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 and;
mod bits_; mod bits_;
mod into;
mod not; mod not;
mod or; mod or;
mod rotate_left; mod rotate_left;
@ -11,7 +10,6 @@ mod xor;
pub use and::BitsAnd; pub use and::BitsAnd;
pub use bits_::Bits; pub use bits_::Bits;
pub use into::BitsInto;
pub use not::BitsNot; pub use not::BitsNot;
pub use or::BitsOr; pub use or::BitsOr;
pub use rotate_left::BitsRol; 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::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathArcCos;
impl Command for SubCommand { impl Command for MathArcCos {
fn name(&self) -> &str { fn name(&self) -> &str {
"math arccos" "math arccos"
} }
@ -114,6 +114,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(MathArcCos {})
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,13 +6,13 @@ mod snake_case;
mod str_; mod str_;
mod title_case; mod title_case;
pub use camel_case::SubCommand as StrCamelCase; pub use camel_case::StrCamelCase;
pub use kebab_case::SubCommand as StrKebabCase; pub use kebab_case::StrKebabCase;
pub use pascal_case::SubCommand as StrPascalCase; pub use pascal_case::StrPascalCase;
pub use screaming_snake_case::SubCommand as StrScreamingSnakeCase; pub use screaming_snake_case::StrScreamingSnakeCase;
pub use snake_case::SubCommand as StrSnakeCase; pub use snake_case::StrSnakeCase;
pub use str_::Str; 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_cmd_base::input_handler::{operate as general_operate, CmdArgument};
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;

View File

@ -3,9 +3,9 @@ use heck::ToUpperCamelCase;
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct StrPascalCase;
impl Command for SubCommand { impl Command for StrPascalCase {
fn name(&self) -> &str { fn name(&self) -> &str {
"str pascal-case" "str pascal-case"
} }
@ -91,6 +91,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct StrScreamingSnakeCase;
impl Command for SubCommand { impl Command for StrScreamingSnakeCase {
fn name(&self) -> &str { fn name(&self) -> &str {
"str screaming-snake-case" "str screaming-snake-case"
} }
@ -91,6 +91,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct StrSnakeCase;
impl Command for SubCommand { impl Command for StrSnakeCase {
fn name(&self) -> &str { fn name(&self) -> &str {
"str snake-case" "str snake-case"
} }
@ -91,6 +91,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct StrTitleCase;
impl Command for SubCommand { impl Command for StrTitleCase {
fn name(&self) -> &str { fn name(&self) -> &str {
"str title-case" "str title-case"
} }
@ -86,6 +86,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-cmd-lang" name = "nu-cmd-lang"
version = "0.102.1" version = "0.103.1"
[lib] [lib]
bench = false bench = false
@ -15,10 +15,10 @@ bench = false
workspace = true workspace = true
[dependencies] [dependencies]
nu-engine = { path = "../nu-engine", 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.102.1" } nu-parser = { path = "../nu-parser", version = "0.103.1" }
nu-protocol = { path = "../nu-protocol", version = "0.102.1", default-features = false } nu-protocol = { path = "../nu-protocol", version = "0.103.1", default-features = false }
nu-utils = { path = "../nu-utils", version = "0.102.1", default-features = false } nu-utils = { path = "../nu-utils", version = "0.103.1", default-features = false }
itertools = { workspace = true } itertools = { workspace = true }
shadow-rs = { version = "0.38", default-features = false } shadow-rs = { version = "0.38", default-features = false }
@ -42,7 +42,6 @@ plugin = [
"os", "os",
] ]
mimalloc = []
trash-support = [] trash-support = []
sqlite = [] sqlite = []
static-link-openssl = [] static-link-openssl = []

View File

@ -103,7 +103,7 @@ impl Command for Describe {
"category" => Value::test_string("default"), "category" => Value::test_string("default"),
)), )),
)), )),
"first_commit" => Value::test_string("date"), "first_commit" => Value::test_string("datetime"),
"my_duration" => Value::test_string("duration"), "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 { fn global_allocator() -> &'static str {
if cfg!(feature = "mimalloc") { "standard"
"mimalloc"
} else {
"standard"
}
} }
fn features_enabled() -> Vec<String> { fn features_enabled() -> Vec<String> {

View File

@ -5,7 +5,7 @@ edition = "2021"
license = "MIT" license = "MIT"
name = "nu-cmd-plugin" name = "nu-cmd-plugin"
repository = "https://github.com/nushell/nushell/tree/main/crates/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 # 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 workspace = true
[dependencies] [dependencies]
nu-engine = { path = "../nu-engine", version = "0.102.1" } nu-engine = { path = "../nu-engine", version = "0.103.1" }
nu-path = { path = "../nu-path", version = "0.102.1" } nu-path = { path = "../nu-path", version = "0.103.1" }
nu-protocol = { path = "../nu-protocol", version = "0.102.1", features = ["plugin"] } nu-protocol = { path = "../nu-protocol", version = "0.103.1", features = ["plugin"] }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.102.1" } nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.103.1" }
itertools = { workspace = true } itertools = { workspace = true }

View File

@ -5,7 +5,7 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-confi
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-color-config" name = "nu-color-config"
version = "0.102.1" version = "0.103.1"
[lib] [lib]
bench = false bench = false
@ -14,12 +14,12 @@ bench = false
workspace = true workspace = true
[dependencies] [dependencies]
nu-protocol = { path = "../nu-protocol", version = "0.102.1", default-features = false } nu-protocol = { path = "../nu-protocol", version = "0.103.1", default-features = false }
nu-engine = { path = "../nu-engine", version = "0.102.1", default-features = false } nu-engine = { path = "../nu-engine", version = "0.103.1", default-features = false }
nu-json = { path = "../nu-json", version = "0.102.1" } nu-json = { path = "../nu-json", version = "0.103.1" }
nu-ansi-term = { workspace = true } nu-ansi-term = { workspace = true }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
[dev-dependencies] [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())), ("int".to_string(), ComputableStyle::Static(Color::White.normal())),
("filesize".to_string(), ComputableStyle::Static(Color::Cyan.normal())), ("filesize".to_string(), ComputableStyle::Static(Color::Cyan.normal())),
("duration".to_string(), ComputableStyle::Static(Color::White.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())), ("range".to_string(), ComputableStyle::Static(Color::White.normal())),
("float".to_string(), ComputableStyle::Static(Color::White.normal())), ("float".to_string(), ComputableStyle::Static(Color::White.normal())),
("string".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" license = "MIT"
name = "nu-command" name = "nu-command"
repository = "https://github.com/nushell/nushell/tree/main/crates/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 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -16,21 +16,21 @@ bench = false
workspace = true workspace = true
[dependencies] [dependencies]
nu-cmd-base = { path = "../nu-cmd-base", version = "0.102.1" } nu-cmd-base = { path = "../nu-cmd-base", version = "0.103.1" }
nu-color-config = { path = "../nu-color-config", version = "0.102.1" } nu-color-config = { path = "../nu-color-config", version = "0.103.1" }
nu-engine = { path = "../nu-engine", version = "0.102.1", default-features = false } nu-engine = { path = "../nu-engine", version = "0.103.1", default-features = false }
nu-glob = { path = "../nu-glob", version = "0.102.1" } nu-glob = { path = "../nu-glob", version = "0.103.1" }
nu-json = { path = "../nu-json", version = "0.102.1" } nu-json = { path = "../nu-json", version = "0.103.1" }
nu-parser = { path = "../nu-parser", version = "0.102.1" } nu-parser = { path = "../nu-parser", version = "0.103.1" }
nu-path = { path = "../nu-path", version = "0.102.1" } nu-path = { path = "../nu-path", version = "0.103.1" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.102.1" } nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.103.1" }
nu-protocol = { path = "../nu-protocol", version = "0.102.1", default-features = false } nu-protocol = { path = "../nu-protocol", version = "0.103.1", default-features = false }
nu-system = { path = "../nu-system", version = "0.102.1" } nu-system = { path = "../nu-system", version = "0.103.1" }
nu-table = { path = "../nu-table", version = "0.102.1" } nu-table = { path = "../nu-table", version = "0.103.1" }
nu-term-grid = { path = "../nu-term-grid", version = "0.102.1" } nu-term-grid = { path = "../nu-term-grid", version = "0.103.1" }
nu-utils = { path = "../nu-utils", version = "0.102.1", default-features = false } nu-utils = { path = "../nu-utils", version = "0.103.1", default-features = false }
nu-ansi-term = { workspace = true } nu-ansi-term = { workspace = true }
nuon = { path = "../nuon", version = "0.102.1" } nuon = { path = "../nuon", version = "0.103.1" }
alphanumeric-sort = { workspace = true } alphanumeric-sort = { workspace = true }
base64 = { workspace = true } base64 = { workspace = true }
@ -209,8 +209,8 @@ sqlite = ["rusqlite"]
trash-support = ["trash"] trash-support = ["trash"]
[dev-dependencies] [dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.102.1" } nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.103.1" }
nu-test-support = { path = "../nu-test-support", version = "0.102.1" } nu-test-support = { path = "../nu-test-support", version = "0.103.1" }
dirs = { workspace = true } dirs = { workspace = true }
mockito = { workspace = true, default-features = false } mockito = { workspace = true, default-features = false }

View File

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

View File

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

View File

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

View File

@ -4,9 +4,9 @@ use nu_protocol::{ast::Expr, Unit};
const NS_PER_SEC: i64 = 1_000_000_000; const NS_PER_SEC: i64 = 1_000_000_000;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct IntoDuration;
impl Command for SubCommand { impl Command for IntoDuration {
fn name(&self) -> &str { fn name(&self) -> &str {
"into duration" "into duration"
} }
@ -277,7 +277,7 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(IntoDuration {})
} }
const NS_PER_SEC: i64 = 1_000_000_000; 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; use nu_utils::get_system_locale;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct IntoFilesize;
impl Command for SubCommand { impl Command for IntoFilesize {
fn name(&self) -> &str { fn name(&self) -> &str {
"into filesize" "into filesize"
} }
@ -197,6 +197,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct IntoFloat;
impl Command for SubCommand { impl Command for IntoFloat {
fn name(&self) -> &str { fn name(&self) -> &str {
"into float" "into float"
} }
@ -134,7 +134,7 @@ mod tests {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(IntoFloat {})
} }
#[test] #[test]

View File

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

View File

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

View File

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

View File

@ -3,9 +3,9 @@ use nu_engine::command_prelude::*;
use nu_protocol::format_duration_as_timeperiod; use nu_protocol::format_duration_as_timeperiod;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct IntoRecord;
impl Command for SubCommand { impl Command for IntoRecord {
fn name(&self) -> &str { fn name(&self) -> &str {
"into record" "into record"
} }
@ -243,6 +243,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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_cmd_base::input_handler::{operate, CmdArgument};
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use nu_protocol::{shell_error::into_code, Config}; use nu_protocol::Config;
use nu_utils::get_system_locale; use nu_utils::get_system_locale;
use num_format::ToFormattedString; use num_format::ToFormattedString;
use std::sync::Arc; use std::sync::Arc;
@ -19,9 +19,9 @@ impl CmdArgument for Arguments {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct IntoString;
impl Command for SubCommand { impl Command for IntoString {
fn name(&self) -> &str { fn name(&self) -> &str {
"into string" "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::Bool { val, .. } => Value::string(val.to_string(), span),
Value::Date { val, .. } => Value::string(val.format("%c").to_string(), span), Value::Date { val, .. } => Value::string(val.format("%c").to_string(), span),
Value::String { val, .. } => { Value::String { val, .. } => Value::string(val, span),
if group_digits { Value::Glob { val, .. } => Value::string(val, span),
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::Filesize { val, .. } => { Value::Filesize { val, .. } => {
if group_digits { if group_digits {
let decimal_value = digits.unwrap_or(0) as usize; 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::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::Nothing { .. } => Value::string("".to_string(), span),
Value::Record { .. } => Value::error( Value::Record { .. } => Value::error(
// Watch out for CantConvert's argument order // 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)) .unwrap_or_else(|err| Value::error(err, span))
} }
Value::Error { .. } => input.clone(),
x => Value::error( x => Value::error(
ShellError::CantConvert { ShellError::CantConvert {
to_type: String::from("string"), to_type: String::from("string"),
@ -316,6 +306,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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) { } 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 { 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(), from_type: "string".to_string(),
span, span,
help: Some(format!( 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)) Ok(Value::date(dt, span))
} else if DATETIME_YMD_RE.is_match(&val_str).unwrap_or(false) { } 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 { 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(), from_type: "string".to_string(),
span, span,
help: Some(format!( 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)) Ok(Value::date(dt, span))
} else if DATETIME_YMDZ_RE.is_match(&val_str).unwrap_or(false) { } 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 { 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(), from_type: "string".to_string(),
span, span,
help: Some(format!( help: Some(format!(

View File

@ -4,4 +4,4 @@ mod split_cell_path;
pub use fill::Fill; pub use fill::Fill;
pub use into::*; 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}; use nu_protocol::{ast::PathMember, IntoValue};
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct SplitCellPath;
impl Command for SubCommand { impl Command for SplitCellPath {
fn name(&self) -> &str { fn name(&self) -> &str {
"split cell-path" "split cell-path"
} }
@ -150,6 +150,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct DateHumanize;
impl Command for SubCommand { impl Command for DateHumanize {
fn name(&self) -> &str { fn name(&self) -> &str {
"date humanize" "date humanize"
} }
@ -101,6 +101,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct DateListTimezones;
impl Command for SubCommand { impl Command for DateListTimezones {
fn name(&self) -> &str { fn name(&self) -> &str {
"date list-timezone" "date list-timezone"
} }

View File

@ -7,8 +7,8 @@ mod to_timezone;
mod utils; mod utils;
pub use date_::Date; pub use date_::Date;
pub use humanize::SubCommand as DateHumanize; pub use humanize::DateHumanize;
pub use list_timezone::SubCommand as DateListTimezones; pub use list_timezone::DateListTimezones;
pub use now::SubCommand as DateNow; pub use now::DateNow;
pub use to_timezone::SubCommand as DateToTimezone; pub use to_timezone::DateToTimezone;
pub(crate) use utils::{generate_strftime_list, parse_date_from_string}; 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::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct DateNow;
impl Command for SubCommand { impl Command for DateNow {
fn name(&self) -> &str { fn name(&self) -> &str {
"date now" "date now"
} }

View File

@ -4,9 +4,9 @@ use chrono::{DateTime, FixedOffset, Local, LocalResult, TimeZone};
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct DateToTimezone;
impl Command for SubCommand { impl Command for DateToTimezone {
fn name(&self) -> &str { fn name(&self) -> &str {
"date to-timezone" "date to-timezone"
} }
@ -147,6 +147,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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, ParEach,
ChunkBy, ChunkBy,
Prepend, Prepend,
Range,
Reduce, Reduce,
Reject, Reject,
Rename, Rename,
@ -455,7 +454,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
Job, Job,
}; };
#[cfg(unix)] #[cfg(all(unix, feature = "os"))]
bind_command! { bind_command! {
JobUnfreeze, JobUnfreeze,
} }

View File

@ -72,7 +72,7 @@ fn convert_string_to_value(
Err(x) => match x { Err(x) => match x {
nu_json::Error::Syntax(_, row, col) => { nu_json::Error::Syntax(_, row, col) => {
let label = x.to_string(); 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 { Err(ShellError::GenericError {
error: "Error while parsing JSON text".into(), error: "Error while parsing JSON text".into(),
msg: "error parsing JSON text".into(), msg: "error parsing JSON text".into(),
@ -173,23 +173,3 @@ fn expand_closure(
_ => None, _ => 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_engine::{command_prelude::*, ClosureEvalOnce};
use nu_protocol::{ use nu_protocol::{
engine::{Closure, Job, ThreadJob}, engine::{Closure, Job, Redirection, ThreadJob},
report_shell_error, Signals, report_shell_error, OutDest, Signals,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -76,15 +76,18 @@ impl Command for JobSpawn {
let result = thread::Builder::new() let result = thread::Builder::new()
.name(format!("background job {}", id.get())) .name(format!("background job {}", id.get()))
.spawn(move || { .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()) .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| { .unwrap_or_else(|err| {
if !job_state.signals().interrupted() { if !job_state.signals().interrupted() {
report_shell_error(&job_state, &err); report_shell_error(&job_state, &err);
} }
Value::nothing(head)
}); });
{ {

View File

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

View File

@ -802,7 +802,7 @@ mod windows_helper {
use std::os::windows::prelude::OsStrExt; use std::os::windows::prelude::OsStrExt;
use windows::Win32::Foundation::FILETIME; use windows::Win32::Foundation::FILETIME;
use windows::Win32::Storage::FileSystem::{ 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, FILE_ATTRIBUTE_REPARSE_POINT, WIN32_FIND_DATAW,
}; };
use windows::Win32::System::SystemServices::{ use windows::Win32::System::SystemServices::{
@ -925,7 +925,14 @@ mod windows_helper {
windows::core::PCWSTR(filename_wide.as_ptr()), windows::core::PCWSTR(filename_wide.as_ptr()),
&mut find_data, &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( Err(e) => Err(ShellError::Io(IoError::new_with_additional_context(
std::io::ErrorKind::Other, std::io::ErrorKind::Other,
span, span,

View File

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

View File

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

View File

@ -23,6 +23,11 @@ impl Command for Default {
SyntaxShape::String, SyntaxShape::String,
"The name of the column.", "The name of the column.",
) )
.switch(
"empty",
"also replace empty items like \"\", {}, and []",
Some('e'),
)
.category(Category::Filters) .category(Category::Filters)
} }
@ -37,7 +42,8 @@ impl Command for Default {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> 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> { 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, stack: &mut Stack,
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
default_when_empty: bool,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let metadata = input.metadata(); let metadata = input.metadata();
let value: Value = call.req(engine_state, stack, 0)?; let value: Value = call.req(engine_state, stack, 0)?;
@ -104,7 +125,9 @@ fn default(
} => { } => {
let record = record.to_mut(); let record = record.to_mut();
if let Some(val) = record.get_mut(&column.item) { 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(); *val = value.clone();
} }
} else { } else {
@ -118,7 +141,10 @@ fn default(
engine_state.signals(), engine_state.signals(),
) )
.map(|x| x.set_metadata(metadata)) .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()) Ok(value.into_pipeline_data())
} else { } else {
Ok(input) Ok(input)

View File

@ -31,7 +31,6 @@ mod merge;
mod move_; mod move_;
mod par_each; mod par_each;
mod prepend; mod prepend;
mod range;
mod reduce; mod reduce;
mod reject; mod reject;
mod rename; mod rename;
@ -91,7 +90,6 @@ pub use merge::MergeDeep;
pub use move_::Move; pub use move_::Move;
pub use par_each::ParEach; pub use par_each::ParEach;
pub use prepend::Prepend; pub use prepend::Prepend;
pub use range::Range;
pub use reduce::Reduce; pub use reduce::Reduce;
pub use reject::Reject; pub use reject::Reject;
pub use rename::Rename; 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> { fn convert_string_to_value(string_input: &str, span: Span) -> Result<Value, ShellError> {
match nu_json::from_str(string_input) { match nu_json::from_str(string_input) {
Ok(value) => Ok(convert_nujson_to_value(value, span)), 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 { Err(x) => match x {
nu_json::Error::Syntax(_, row, col) => { nu_json::Error::Syntax(_, row, col) => {
let label = x.to_string(); 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 { Err(ShellError::GenericError {
error: "Error while parsing JSON text".into(), error: "Error while parsing JSON text".into(),
msg: "error 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)), Ok(value) => Ok(convert_nujson_to_value(value, span)),
Err(err) => Err(if err.is_syntax() { Err(err) => Err(if err.is_syntax() {
let label = err.to_string(); 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 { ShellError::GenericError {
error: "Error while parsing JSON text".into(), error: "Error while parsing JSON text".into(),
msg: "error 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 indexmap::IndexMap;
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use roxmltree::NodeType; use roxmltree::{NodeType, ParsingOptions, TextPos};
#[derive(Clone)] #[derive(Clone)]
pub struct FromXml; pub struct FromXml;
@ -16,6 +16,11 @@ impl Command for FromXml {
Signature::build("from xml") Signature::build("from xml")
.input_output_types(vec![(Type::String, Type::record())]) .input_output_types(vec![(Type::String, Type::record())])
.switch("keep-comments", "add comment nodes to result", None) .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( .switch(
"keep-pi", "keep-pi",
"add processing instruction nodes to result", "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 head = call.head;
let keep_comments = call.has_flag(engine_state, stack, "keep-comments")?; let keep_comments = call.has_flag(engine_state, stack, "keep-comments")?;
let keep_processing_instructions = call.has_flag(engine_state, stack, "keep-pi")?; 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 { let info = ParsingInfo {
span: head, span: head,
keep_comments, keep_comments,
keep_processing_instructions, keep_processing_instructions,
allow_dtd,
}; };
from_xml(input, &info) from_xml(input, &info)
} }
@ -90,6 +97,7 @@ struct ParsingInfo {
span: Span, span: Span,
keep_comments: bool, keep_comments: bool,
keep_processing_instructions: bool, keep_processing_instructions: bool,
allow_dtd: bool,
} }
fn from_attributes_to_value(attributes: &[roxmltree::Attribute], info: &ParsingInfo) -> Value { 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> { 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)) 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) => {
Ok(x.into_pipeline_data_with_metadata(metadata.map(|md| md.with_content_type(None)))) 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 { 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.", "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.", "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.", "The http://www.w3.org/2000/xmlns/ URI must not be declared.",
span, source, pos,
), ),
roxmltree::Error::InvalidElementNamePrefix(_) => { roxmltree::Error::InvalidElementNamePrefix(pos) => {
make_cant_convert_error("xmlns can't be used as an element prefix.", span) make_xml_error_spanned("xmlns can't be used as an element prefix.", source, pos)
} }
roxmltree::Error::DuplicatedNamespace(_, _) => { roxmltree::Error::DuplicatedNamespace(namespace, pos) => {
make_cant_convert_error("A namespace was already defined on this element.", span) make_xml_error_spanned(format!("Namespace {namespace} was already defined on this element."), source, pos)
} }
roxmltree::Error::UnknownNamespace(prefix, _) => { roxmltree::Error::UnknownNamespace(prefix, pos) => {
make_cant_convert_error(format!("Unknown prefix {}", prefix), span) make_xml_error_spanned(format!("Unknown prefix {}", prefix), source, pos)
} }
roxmltree::Error::UnexpectedCloseTag { .. } => { roxmltree::Error::UnexpectedCloseTag(expected, actual, pos) => {
make_cant_convert_error("Unexpected close tag", span) make_xml_error_spanned(format!("Unexpected close tag {actual}, expected {expected}"), source, pos)
} }
roxmltree::Error::UnexpectedEntityCloseTag(_) => { roxmltree::Error::UnexpectedEntityCloseTag(pos) => {
make_cant_convert_error("Entity value starts with a close tag.", span) make_xml_error_spanned("Entity value starts with a close tag.", source, pos)
} }
roxmltree::Error::UnknownEntityReference(_, _) => make_cant_convert_error( roxmltree::Error::UnknownEntityReference(entity, pos) => make_xml_error_spanned(
"A reference to an entity that was not defined in the DTD.", format!("Reference to unknown entity {entity} (was not defined in the DTD)"),
span, source, pos,
), ),
roxmltree::Error::MalformedEntityReference(_) => { roxmltree::Error::MalformedEntityReference(pos) => {
make_cant_convert_error("A malformed entity reference.", span) make_xml_error_spanned("Malformed entity reference.", source, pos)
} }
roxmltree::Error::EntityReferenceLoop(_) => { roxmltree::Error::EntityReferenceLoop(pos) => {
make_cant_convert_error("A possible entity reference loop.", span) make_xml_error_spanned("Possible entity reference loop.", source, pos)
} }
roxmltree::Error::InvalidAttributeValue(_) => { roxmltree::Error::InvalidAttributeValue(pos) => {
make_cant_convert_error("Attribute value cannot have a < character.", span) make_xml_error_spanned("Attribute value cannot have a < character.", source, pos)
} }
roxmltree::Error::DuplicatedAttribute(_, _) => { roxmltree::Error::DuplicatedAttribute(attribute, pos) => {
make_cant_convert_error("An element has a duplicated attributes.", span) make_xml_error_spanned(format!("Element has a duplicated attribute: {attribute}"), source, pos)
} }
roxmltree::Error::NoRootNode => { 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 => { 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( roxmltree::Error::DtdDetected => make_xml_error(
"An XML with DTD detected. DTDs are currently disabled due to security reasons.", "XML document with DTD detected.\nDTDs are disabled by default to prevent denial-of-service attacks (use --allow-dtd to parse anyway)",
span, span
), ),
roxmltree::Error::NodesLimitReached => { roxmltree::Error::NodesLimitReached => {
make_cant_convert_error("Node limit was reached.", span) make_xml_error("Node limit was reached.", span)
} }
roxmltree::Error::AttributesLimitReached => { roxmltree::Error::AttributesLimitReached => {
make_cant_convert_error("Attribute limit reached", span) make_xml_error("Attribute limit reached", span)
} }
roxmltree::Error::NamespacesLimitReached => { roxmltree::Error::NamespacesLimitReached => {
make_cant_convert_error("Namespace limit reached", span) make_xml_error("Namespace limit reached", span)
} }
roxmltree::Error::UnexpectedDeclaration(_) => { roxmltree::Error::UnexpectedDeclaration(pos) => {
make_cant_convert_error("An XML document can have only one XML declaration and it must be at the start of the document.", span) 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(_) => { roxmltree::Error::InvalidName(pos) => {
make_cant_convert_error("Invalid name found.", span) make_xml_error_spanned("Invalid name.", source, pos)
} }
roxmltree::Error::NonXmlChar(_, _) => { roxmltree::Error::NonXmlChar(_, pos) => {
make_cant_convert_error("A non-XML character has occurred. Valid characters are: <https://www.w3.org/TR/xml/#char32>", span) make_xml_error_spanned("Non-XML character found. Valid characters are: <https://www.w3.org/TR/xml/#char32>", source, pos)
} }
roxmltree::Error::InvalidChar(_, _, _) => { roxmltree::Error::InvalidChar(expected, actual, pos) => {
make_cant_convert_error("An invalid/unexpected character in XML.", span) make_xml_error_spanned(
format!("Unexpected character {}, expected {}", actual as char, expected as char),
source,
pos
)
} }
roxmltree::Error::InvalidChar2(_, _, _) => { roxmltree::Error::InvalidChar2(expected, actual, pos) => {
make_cant_convert_error("An invalid/unexpected character in XML.", span) make_xml_error_spanned(
format!("Unexpected character {}, expected {}", actual as char, expected),
source,
pos
)
} }
roxmltree::Error::InvalidString(_, _) => { roxmltree::Error::InvalidString(_, pos) => {
make_cant_convert_error("An invalid/unexpected string in XML.", span) make_xml_error_spanned("Invalid/unexpected string in XML.", source, pos)
} }
roxmltree::Error::InvalidExternalID(_) => { roxmltree::Error::InvalidExternalID(pos) => {
make_cant_convert_error("An invalid ExternalID in the DTD.", span) make_xml_error_spanned("Invalid ExternalID in the DTD.", source, pos)
} }
roxmltree::Error::InvalidComment(_) => { roxmltree::Error::InvalidComment(pos) => {
make_cant_convert_error("A comment cannot contain `--` or end with `-`.", span) make_xml_error_spanned("A comment cannot contain `--` or end with `-`.", source, pos)
} }
roxmltree::Error::InvalidCharacterData(_) => { roxmltree::Error::InvalidCharacterData(pos) => {
make_cant_convert_error("A Character Data node contains an invalid data. Currently, only `]]>` is not allowed.", span) make_xml_error_spanned("Character Data node contains an invalid data. Currently, only `]]>` is not allowed.", source, pos)
} }
roxmltree::Error::UnknownToken(_) => { roxmltree::Error::UnknownToken(pos) => {
make_cant_convert_error("Unknown token in XML.", span) make_xml_error_spanned("Unknown token in XML.", source, pos)
} }
roxmltree::Error::UnexpectedEndOfStream => { 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 { fn make_xml_error(msg: impl Into<String>, span: Span) -> ShellError {
ShellError::CantConvert { ShellError::GenericError {
from_type: Type::String.to_string(), error: "Failed to parse XML".into(),
to_type: "XML".to_string(), 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, span,
help: Some(help.into()),
} }
} }
@ -375,6 +407,7 @@ mod tests {
span: Span::test_data(), span: Span::test_data(),
keep_comments: false, keep_comments: false,
keep_processing_instructions: false, keep_processing_instructions: false,
allow_dtd: false,
}; };
from_xml_string_to_value(xml, &info) 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 // allow ranges to expand and turn into array
let input = input.try_expand_range()?; let input = input.try_expand_range()?;
let value = input.into_value(span)?; 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 { let json_result = if raw {
nu_json::to_string_raw(&json_value) nu_json::to_string_raw(&json_value)
@ -78,16 +78,12 @@ impl Command for ToJson {
}; };
Ok(PipelineData::Value(res, Some(metadata))) Ok(PipelineData::Value(res, Some(metadata)))
} }
_ => Ok(Value::error( _ => Err(ShellError::CantConvert {
ShellError::CantConvert { to_type: "JSON".into(),
to_type: "JSON".into(), from_type: value.get_type().to_string(),
from_type: value.get_type().to_string(),
span,
help: None,
},
span, span,
) help: None,
.into_pipeline_data()), }),
} }
} }
@ -118,6 +114,7 @@ impl Command for ToJson {
pub fn value_to_json_value( pub fn value_to_json_value(
engine_state: &EngineState, engine_state: &EngineState,
v: &Value, v: &Value,
call_span: Span,
serialize_types: bool, serialize_types: bool,
) -> Result<nu_json::Value, ShellError> { ) -> Result<nu_json::Value, ShellError> {
let span = v.span(); let span = v.span();
@ -142,24 +139,20 @@ pub fn value_to_json_value(
), ),
Value::List { vals, .. } => { 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::Error { error, .. } => return Err(*error.clone()),
Value::Closure { val, .. } => { Value::Closure { val, .. } => {
if serialize_types { if serialize_types {
let block = engine_state.get_block(val.block_id); let closure_string = val.coerce_into_string(engine_state, span)?;
if let Some(span) = block.span { nu_json::Value::String(closure_string.to_string())
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()
))
}
} else { } 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, Value::Range { .. } => nu_json::Value::Null,
@ -171,14 +164,14 @@ pub fn value_to_json_value(
for (k, v) in &**val { for (k, v) in &**val {
m.insert( m.insert(
k.clone(), 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) nu_json::Value::Object(m)
} }
Value::Custom { val, .. } => { Value::Custom { val, .. } => {
let collected = val.to_base_value(span)?; 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( fn json_list(
engine_state: &EngineState, engine_state: &EngineState,
input: &[Value], input: &[Value],
call_span: Span,
serialize_types: bool, serialize_types: bool,
) -> Result<Vec<nu_json::Value>, ShellError> { ) -> Result<Vec<nu_json::Value>, ShellError> {
let mut out = vec![]; let mut out = vec![];
for value in input { 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) Ok(out)

View File

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

View File

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

View File

@ -69,16 +69,9 @@ impl Command for ToNuon {
match nuon::to_nuon(engine_state, &value, style, Some(span), serialize_types) { match nuon::to_nuon(engine_state, &value, style, Some(span), serialize_types) {
Ok(serde_nuon_string) => Ok(Value::string(serde_nuon_string, span) Ok(serde_nuon_string) => Ok(Value::string(serde_nuon_string, span)
.into_pipeline_data_with_metadata(Some(metadata))), .into_pipeline_data_with_metadata(Some(metadata))),
_ => Ok(Value::error( Err(error) => {
ShellError::CantConvert { Ok(Value::error(error, span).into_pipeline_data_with_metadata(Some(metadata)))
to_type: "NUON".into(), }
from_type: value.get_type().to_string(),
span,
help: None,
},
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_engine::command_prelude::*;
use nu_protocol::Range;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathAbs;
impl Command for SubCommand { impl Command for MathAbs {
fn name(&self) -> &str { fn name(&self) -> &str {
"math abs" "math abs"
} }
@ -21,6 +23,7 @@ impl Command for SubCommand {
Type::List(Box::new(Type::Duration)), Type::List(Box::new(Type::Duration)),
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) .allow_variants_without_examples(true)
.category(Category::Math) .category(Category::Math)
@ -46,6 +49,19 @@ impl Command for SubCommand {
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head; 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()) input.map(move |value| abs_helper(value, head), engine_state.signals())
} }
@ -56,6 +72,19 @@ impl Command for SubCommand {
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head; 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( input.map(
move |value| abs_helper(value, head), move |value| abs_helper(value, head),
working_set.permanent().signals(), working_set.permanent().signals(),
@ -105,6 +134,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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; const NS_PER_SEC: i64 = 1_000_000_000;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathAvg;
impl Command for SubCommand { impl Command for MathAvg {
fn name(&self) -> &str { fn name(&self) -> &str {
"math avg" "math avg"
} }
@ -104,6 +104,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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_engine::command_prelude::*;
use nu_protocol::Range;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathCeil;
impl Command for SubCommand { impl Command for MathCeil {
fn name(&self) -> &str { fn name(&self) -> &str {
"math ceil" "math ceil"
} }
@ -16,6 +18,7 @@ impl Command for SubCommand {
Type::List(Box::new(Type::Number)), Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Int)), Type::List(Box::new(Type::Int)),
), ),
(Type::Range, Type::List(Box::new(Type::Number))),
]) ])
.allow_variants_without_examples(true) .allow_variants_without_examples(true)
.category(Category::Math) .category(Category::Math)
@ -45,6 +48,19 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) { if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head }); 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()) input.map(move |value| operate(value, head), engine_state.signals())
} }
@ -59,6 +75,19 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) { if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head }); 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( input.map(
move |value| operate(value, head), move |value| operate(value, head),
working_set.permanent().signals(), working_set.permanent().signals(),
@ -103,6 +132,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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_engine::command_prelude::*;
use nu_protocol::Range;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathFloor;
impl Command for SubCommand { impl Command for MathFloor {
fn name(&self) -> &str { fn name(&self) -> &str {
"math floor" "math floor"
} }
@ -16,6 +18,7 @@ impl Command for SubCommand {
Type::List(Box::new(Type::Number)), Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Int)), Type::List(Box::new(Type::Int)),
), ),
(Type::Range, Type::List(Box::new(Type::Number))),
]) ])
.allow_variants_without_examples(true) .allow_variants_without_examples(true)
.category(Category::Math) .category(Category::Math)
@ -45,6 +48,19 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) { if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head }); 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()) input.map(move |value| operate(value, head), engine_state.signals())
} }
@ -59,6 +75,19 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) { if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head }); 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( input.map(
move |value| operate(value, head), move |value| operate(value, head),
working_set.permanent().signals(), working_set.permanent().signals(),
@ -103,6 +132,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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_engine::command_prelude::*;
use nu_protocol::Range;
use nu_protocol::Signals; use nu_protocol::Signals;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathLog;
impl Command for SubCommand { impl Command for MathLog {
fn name(&self) -> &str { fn name(&self) -> &str {
"math log" "math log"
} }
@ -22,6 +24,7 @@ impl Command for SubCommand {
Type::List(Box::new(Type::Number)), Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Float)), Type::List(Box::new(Type::Float)),
), ),
(Type::Range, Type::List(Box::new(Type::Number))),
]) ])
.allow_variants_without_examples(true) .allow_variants_without_examples(true)
.category(Category::Math) .category(Category::Math)
@ -46,7 +49,21 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head;
let base: Spanned<f64> = call.req(engine_state, stack, 0)?; 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()) log(base, call.head, input, engine_state.signals())
} }
@ -56,7 +73,21 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head;
let base: Spanned<f64> = call.req_const(working_set, 0)?; 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()) log(base, call.head, input, working_set.permanent().signals())
} }
@ -159,6 +190,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathMax;
impl Command for SubCommand { impl Command for MathMax {
fn name(&self) -> &str { fn name(&self) -> &str {
"math max" "math max"
} }
@ -97,6 +97,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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; use std::cmp::Ordering;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathMedian;
impl Command for SubCommand { impl Command for MathMedian {
fn name(&self) -> &str { fn name(&self) -> &str {
"math median" "math median"
} }
@ -151,6 +151,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathMin;
impl Command for SubCommand { impl Command for MathMin {
fn name(&self) -> &str { fn name(&self) -> &str {
"math min" "math min"
} }
@ -95,6 +95,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::test_examples; use crate::test_examples;
test_examples(SubCommand {}) test_examples(MathMin {})
} }
} }

View File

@ -17,20 +17,20 @@ mod sum;
mod utils; mod utils;
mod variance; mod variance;
pub use abs::SubCommand as MathAbs; pub use abs::MathAbs;
pub use avg::SubCommand as MathAvg; pub use avg::MathAvg;
pub use ceil::SubCommand as MathCeil; pub use ceil::MathCeil;
pub use floor::SubCommand as MathFloor; pub use floor::MathFloor;
pub use math_::MathCommand as Math; pub use math_::MathCommand as Math;
pub use max::SubCommand as MathMax; pub use max::MathMax;
pub use median::SubCommand as MathMedian; pub use median::MathMedian;
pub use min::SubCommand as MathMin; pub use min::MathMin;
pub use mode::SubCommand as MathMode; pub use mode::MathMode;
pub use product::SubCommand as MathProduct; pub use product::MathProduct;
pub use round::SubCommand as MathRound; pub use round::MathRound;
pub use sqrt::SubCommand as MathSqrt; pub use sqrt::MathSqrt;
pub use stddev::SubCommand as MathStddev; pub use stddev::MathStddev;
pub use sum::SubCommand as MathSum; pub use sum::MathSum;
pub use variance::SubCommand as MathVariance; 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}; use std::{cmp::Ordering, collections::HashMap};
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathMode;
#[derive(Hash, Eq, PartialEq, Debug)] #[derive(Hash, Eq, PartialEq, Debug)]
enum NumberTypes { enum NumberTypes {
@ -28,7 +28,7 @@ impl HashableType {
} }
} }
impl Command for SubCommand { impl Command for MathMode {
fn name(&self) -> &str { fn name(&self) -> &str {
"math mode" "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 //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 // But f64 doesn't implement Hash, so we get the binary representation to use as
// key in the HashMap // key in the HashMap
@ -130,11 +130,11 @@ pub fn mode(values: &[Value], _span: Span, head: Span) -> Result<Value, ShellErr
NumberTypes::Filesize, NumberTypes::Filesize,
)), )),
Value::Error { error, .. } => Err(*error.clone()), Value::Error { error, .. } => Err(*error.clone()),
other => Err(ShellError::UnsupportedInput { _ => Err(ShellError::UnsupportedInput {
msg: "Unable to give a result with this input".to_string(), msg: "Unable to give a result with this input".to_string(),
input: "value originates from here".into(), input: "value originates from here".into(),
msg_span: head, msg_span: head,
input_span: other.span(), input_span: span,
}), }),
}) })
.collect::<Result<Vec<HashableType>, ShellError>>()?; .collect::<Result<Vec<HashableType>, ShellError>>()?;
@ -183,6 +183,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathProduct;
impl Command for SubCommand { impl Command for MathProduct {
fn name(&self) -> &str { fn name(&self) -> &str {
"math product" "math product"
} }
@ -88,6 +88,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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_engine::command_prelude::*;
use nu_protocol::Range;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathRound;
impl Command for SubCommand { impl Command for MathRound {
fn name(&self) -> &str { fn name(&self) -> &str {
"math round" "math round"
} }
@ -16,6 +18,7 @@ impl Command for SubCommand {
Type::List(Box::new(Type::Number)), Type::List(Box::new(Type::Number)),
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) .allow_variants_without_examples(true)
.named( .named(
@ -52,6 +55,19 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) { if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head }); 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( input.map(
move |value| operate(value, head, precision_param), move |value| operate(value, head, precision_param),
engine_state.signals(), engine_state.signals(),
@ -70,6 +86,19 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) { if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head }); 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( input.map(
move |value| operate(value, head, precision_param), move |value| operate(value, head, precision_param),
working_set.permanent().signals(), working_set.permanent().signals(),
@ -153,6 +182,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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_engine::command_prelude::*;
use nu_protocol::Range;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathSqrt;
impl Command for SubCommand { impl Command for MathSqrt {
fn name(&self) -> &str { fn name(&self) -> &str {
"math sqrt" "math sqrt"
} }
@ -16,6 +18,7 @@ impl Command for SubCommand {
Type::List(Box::new(Type::Number)), Type::List(Box::new(Type::Number)),
Type::List(Box::new(Type::Float)), Type::List(Box::new(Type::Float)),
), ),
(Type::Range, Type::List(Box::new(Type::Number))),
]) ])
.allow_variants_without_examples(true) .allow_variants_without_examples(true)
.category(Category::Math) .category(Category::Math)
@ -45,6 +48,19 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) { if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head }); 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()) input.map(move |value| operate(value, head), engine_state.signals())
} }
@ -59,6 +75,19 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) { if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head }); 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( input.map(
move |value| operate(value, head), move |value| operate(value, head),
working_set.permanent().signals(), working_set.permanent().signals(),
@ -127,6 +156,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct MathStddev;
impl Command for SubCommand { impl Command for MathStddev {
fn name(&self) -> &str { fn name(&self) -> &str {
"math stddev" "math stddev"
} }
@ -14,6 +14,7 @@ impl Command for SubCommand {
Signature::build("math stddev") Signature::build("math stddev")
.input_output_types(vec![ .input_output_types(vec![
(Type::List(Box::new(Type::Number)), Type::Number), (Type::List(Box::new(Type::Number)), Type::Number),
(Type::Range, Type::Number),
(Type::table(), Type::record()), (Type::table(), Type::record()),
(Type::record(), Type::record()), (Type::record(), Type::record()),
]) ])
@ -53,6 +54,18 @@ impl Command for SubCommand {
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let sample = call.has_flag(engine_state, stack, "sample")?; 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)) run_with_function(call, input, compute_stddev(sample))
} }
@ -63,6 +76,18 @@ impl Command for SubCommand {
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let sample = call.has_flag_const(working_set, "sample")?; 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)) run_with_function(call, input, compute_stddev(sample))
} }
@ -111,6 +136,6 @@ mod test {
fn test_examples() { fn test_examples() {
use crate::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