Compare commits

..

1 Commits

Author SHA1 Message Date
Darren Schroeder
286a6b021c Revert "Add format meta command (#11334)"
This reverts commit fd77114d82.
2023-12-15 06:25:37 -06:00
149 changed files with 1712 additions and 3209 deletions

1
.github/.typos.toml vendored
View File

@ -12,4 +12,3 @@ IIF = "IIF"
numer = "numer"
ratatui = "ratatui"
doas = "doas"
wheres = "wheres"

View File

@ -92,17 +92,6 @@ jobs:
- name: Tests
run: cargo test --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
- 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
std-lib-and-python-virtualenv:
strategy:
fail-fast: true
@ -140,17 +129,6 @@ jobs:
run: nu scripts/test_virtualenv.nu
shell: bash
- 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
plugins:
strategy:
fail-fast: true
@ -172,14 +150,3 @@ jobs:
- name: Tests
run: cargo test --profile ci --package 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

View File

@ -119,7 +119,7 @@ jobs:
os: ubuntu-20.04
target_rustflags: ''
- target: riscv64gc-unknown-linux-gnu
os: ubuntu-latest
os: ubuntu-20.04
target_rustflags: ''
runs-on: ${{matrix.os}}

View File

@ -82,8 +82,8 @@ print $'Start building ($bin)...'; hr-line
# ----------------------------------------------------------------------------
# Build for Ubuntu and macOS
# ----------------------------------------------------------------------------
if $os in [$USE_UBUNTU, 'macos-latest', 'ubuntu-latest'] {
if $os starts-with ubuntu {
if $os in [$USE_UBUNTU, 'macos-latest'] {
if $os == $USE_UBUNTU {
sudo apt update
sudo apt-get install libxcb-composite0-dev -y
}
@ -106,7 +106,7 @@ if $os in [$USE_UBUNTU, 'macos-latest', 'ubuntu-latest'] {
_ => {
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
# Actually just for x86_64-unknown-linux-musl target
if $os starts-with ubuntu { sudo apt install musl-tools -y }
if $os == $USE_UBUNTU { sudo apt install musl-tools -y }
cargo-build-nu $flags
}
}
@ -153,7 +153,7 @@ if ($ver | str trim | is-empty) {
# Create a release archive and send it to output for the following steps
# ----------------------------------------------------------------------------
cd $dist; print $'(char nl)Creating release archive...'; hr-line
if $os in [$USE_UBUNTU, 'macos-latest', 'ubuntu-latest'] {
if $os in [$USE_UBUNTU, 'macos-latest'] {
let files = (ls | get name)
let dest = if $env.RELEASE_TYPE == 'full' { $'($bin)-($version)-($FULL_NAME)' } else { $'($bin)-($version)-($target)' }

View File

@ -66,7 +66,7 @@ jobs:
os: ubuntu-20.04
target_rustflags: ''
- target: riscv64gc-unknown-linux-gnu
os: ubuntu-latest
os: ubuntu-20.04
target_rustflags: ''
runs-on: ${{matrix.os}}

View File

@ -10,6 +10,6 @@ jobs:
uses: actions/checkout@v4
- name: Check spelling
uses: crate-ci/typos@v1.17.0
uses: crate-ci/typos@v1.16.24
with:
config: ./.github/.typos.toml

179
Cargo.lock generated
View File

@ -1252,6 +1252,16 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "fancy-regex"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2"
dependencies = [
"bit-set",
"regex",
]
[[package]]
name = "fancy-regex"
version = "0.12.0"
@ -1281,7 +1291,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5"
dependencies = [
"cfg-if",
"rustix",
"rustix 0.38.28",
"windows-sys 0.48.0",
]
@ -1788,7 +1798,7 @@ dependencies = [
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core 0.51.1",
"windows-core",
]
[[package]]
@ -1802,9 +1812,9 @@ dependencies = [
[[package]]
name = "ical"
version = "0.9.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26393c372d4c4d51616084afe36c0b44e4467febaa6f91f11f789094b4863bf9"
checksum = "356d82bd58997815d55ea6f9081bd4cac149e50ca943f7a4f7c050fec7271c1f"
dependencies = [
"thiserror",
]
@ -1884,6 +1894,17 @@ version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0508c56cfe9bfd5dfeb0c22ab9a6abfda2f27bdca422132e494266351ed8d83c"
[[package]]
name = "io-lifetimes"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
"hermit-abi",
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "is-docker"
version = "0.2.0"
@ -1900,7 +1921,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [
"hermit-abi",
"rustix",
"rustix 0.38.28",
"windows-sys 0.48.0",
]
@ -1953,15 +1974,6 @@ dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.10"
@ -2222,6 +2234,12 @@ dependencies = [
"serde",
]
[[package]]
name = "linux-raw-sys"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
[[package]]
name = "linux-raw-sys"
version = "0.4.12"
@ -2255,9 +2273,9 @@ dependencies = [
[[package]]
name = "lscolors"
version = "0.16.0"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab0b209ec3976527806024406fe765474b9a1750a0ed4b8f0372364741f50e7b"
checksum = "bf7015a04103ad78abb77e4b79ed151e767922d1cfde5f62640471c629a2320d"
dependencies = [
"nu-ansi-term",
]
@ -2276,9 +2294,9 @@ dependencies = [
[[package]]
name = "lsp-types"
version = "0.95.0"
version = "0.94.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "158c1911354ef73e8fe42da6b10c0484cb65c7f1007f28022e847706c1ab6984"
checksum = "c66bfd44a06ae10647fe3f8214762e9369fd4248df1350924b4ef9e770a85ea1"
dependencies = [
"bitflags 1.3.2",
"serde",
@ -2659,7 +2677,7 @@ version = "0.88.2"
dependencies = [
"chrono",
"crossterm",
"fancy-regex",
"fancy-regex 0.11.0",
"fuzzy-matcher",
"is_executable",
"log",
@ -2680,7 +2698,7 @@ dependencies = [
"percent-encoding",
"reedline",
"rstest",
"sysinfo 0.30.4",
"sysinfo",
"unicode-segmentation",
"uuid",
"which 5.0.0",
@ -2708,7 +2726,7 @@ version = "0.88.2"
dependencies = [
"chrono",
"chrono-tz",
"fancy-regex",
"fancy-regex 0.12.0",
"indexmap",
"nu-cmd-lang",
"nu-engine",
@ -2730,7 +2748,7 @@ name = "nu-cmd-extra"
version = "0.88.2"
dependencies = [
"ahash",
"fancy-regex",
"fancy-regex 0.11.0",
"heck",
"htmlescape",
"nu-ansi-term",
@ -2754,8 +2772,8 @@ dependencies = [
name = "nu-cmd-lang"
version = "0.88.2"
dependencies = [
"fancy-regex",
"itertools 0.12.0",
"fancy-regex 0.11.0",
"itertools 0.11.0",
"nu-ansi-term",
"nu-engine",
"nu-parser",
@ -2798,7 +2816,7 @@ dependencies = [
"dirs-next",
"dtparse",
"encoding_rs",
"fancy-regex",
"fancy-regex 0.11.0",
"filesize",
"filetime",
"fs_extra",
@ -2806,7 +2824,7 @@ dependencies = [
"human-date-parser",
"indexmap",
"indicatif",
"itertools 0.12.0",
"itertools 0.11.0",
"libc",
"log",
"lscolors",
@ -2859,7 +2877,7 @@ dependencies = [
"serde_urlencoded",
"serde_yaml",
"sha2",
"sysinfo 0.30.4",
"sysinfo",
"tabled",
"terminal_size 0.3.0",
"titlecase",
@ -2876,7 +2894,7 @@ dependencies = [
"uuid",
"wax",
"which 5.0.0",
"windows 0.52.0",
"windows 0.48.0",
"winreg",
]
@ -2907,7 +2925,7 @@ dependencies = [
"nu-utils",
"ratatui",
"strip-ansi-escapes",
"terminal_size 0.3.0",
"terminal_size 0.2.6",
"unicode-width",
]
@ -2955,7 +2973,7 @@ version = "0.88.2"
dependencies = [
"bytesize",
"chrono",
"itertools 0.12.0",
"itertools 0.11.0",
"log",
"nu-engine",
"nu-path",
@ -3002,7 +3020,7 @@ dependencies = [
"byte-unit",
"chrono",
"chrono-humanize",
"fancy-regex",
"fancy-regex 0.11.0",
"indexmap",
"lru",
"miette",
@ -3043,15 +3061,15 @@ dependencies = [
"ntapi",
"once_cell",
"procfs",
"sysinfo 0.30.4",
"windows 0.52.0",
"sysinfo",
"winapi",
]
[[package]]
name = "nu-table"
version = "0.88.2"
dependencies = [
"fancy-regex",
"fancy-regex 0.11.0",
"nu-ansi-term",
"nu-color-config",
"nu-engine",
@ -3396,9 +3414,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-src"
version = "300.1.6+3.1.4"
version = "300.2.1+3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439fac53e092cd7442a3660c85dde4643ab3b5bd39040912388dcdabf6b88085"
checksum = "3fe476c29791a5ca0d1273c697e96085bbabbbea2ef7afd5617e78a4b40332d3"
dependencies = [
"cc",
]
@ -4072,7 +4090,7 @@ dependencies = [
"polars-error",
"rayon",
"smartstring",
"sysinfo 0.29.11",
"sysinfo",
"version_check",
]
@ -4177,7 +4195,7 @@ dependencies = [
"hex",
"lazy_static",
"procfs-core",
"rustix",
"rustix 0.38.28",
]
[[package]]
@ -4374,13 +4392,14 @@ dependencies = [
[[package]]
name = "reedline"
version = "0.27.1"
source = "git+https://github.com/nushell/reedline.git?branch=main#b68ce33c750b700bb4f8adccbc2e160d1b7c8742"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147452ce32c2cba4900b410f1d18b9ca29b67301a8eed76676e42e720d71cc39"
dependencies = [
"chrono",
"crossterm",
"fd-lock",
"itertools 0.12.0",
"itertools 0.10.5",
"nu-ansi-term",
"rusqlite",
"serde",
@ -4627,6 +4646,20 @@ dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "0.37.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2"
dependencies = [
"bitflags 1.3.2",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys 0.3.8",
"windows-sys 0.48.0",
]
[[package]]
name = "rustix"
version = "0.38.28"
@ -4636,7 +4669,7 @@ dependencies = [
"bitflags 2.4.1",
"errno",
"libc",
"linux-raw-sys",
"linux-raw-sys 0.4.12",
"windows-sys 0.52.0",
]
@ -4868,9 +4901,9 @@ dependencies = [
[[package]]
name = "shadow-rs"
version = "0.26.0"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "878cb1e3162d98ee1016b832efbb683ad6302b462a2894c54f488dc0bd96f11c"
checksum = "f9198caff1c94f1a5df6664bddbc379896b51b98a55b0b3fedcb23078fe00c77"
dependencies = [
"const_format",
"is_debug",
@ -5243,20 +5276,6 @@ name = "sysinfo"
version = "0.29.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd727fc423c2060f6c92d9534cef765c65a6ed3f428a03d7def74a8c4348e666"
dependencies = [
"cfg-if",
"core-foundation-sys",
"libc",
"ntapi",
"once_cell",
"winapi",
]
[[package]]
name = "sysinfo"
version = "0.30.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "717570a2533606f81f8cfac02a1915a620e725ffb78f6fc5e259769a4d747407"
dependencies = [
"cfg-if",
"core-foundation-sys",
@ -5264,7 +5283,7 @@ dependencies = [
"ntapi",
"once_cell",
"rayon",
"windows 0.52.0",
"winapi",
]
[[package]]
@ -5294,7 +5313,7 @@ dependencies = [
"cfg-if",
"fastrand",
"redox_syscall",
"rustix",
"rustix 0.38.28",
"windows-sys 0.48.0",
]
@ -5328,13 +5347,23 @@ dependencies = [
"winapi",
]
[[package]]
name = "terminal_size"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
dependencies = [
"rustix 0.37.27",
"windows-sys 0.48.0",
]
[[package]]
name = "terminal_size"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
dependencies = [
"rustix",
"rustix 0.38.28",
"windows-sys 0.48.0",
]
@ -5718,9 +5747,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "unsafe-libyaml"
version = "0.2.10"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b"
checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa"
[[package]]
name = "ureq"
@ -6042,7 +6071,7 @@ dependencies = [
"either",
"home",
"once_cell",
"rustix",
"rustix 0.38.28",
]
[[package]]
@ -6054,7 +6083,7 @@ dependencies = [
"either",
"home",
"once_cell",
"rustix",
"rustix 0.38.28",
"windows-sys 0.48.0",
]
@ -6109,12 +6138,11 @@ dependencies = [
[[package]]
name = "windows"
version = "0.52.0"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
"windows-core 0.52.0",
"windows-targets 0.52.0",
"windows-targets 0.48.5",
]
[[package]]
@ -6126,15 +6154,6 @@ dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.0",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
@ -6369,8 +6388,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7dae5072fe1f8db8f8d29059189ac175196e410e40ba42d5d4684ae2f750995"
dependencies = [
"libc",
"linux-raw-sys",
"rustix",
"linux-raw-sys 0.4.12",
"rustix 0.38.28",
]
[[package]]

View File

@ -33,11 +33,7 @@ members = [
"crates/nu-cmd-lang",
"crates/nu-cmd-dataframe",
"crates/nu-command",
"crates/nu-color-config",
"crates/nu-explore",
"crates/nu-json",
"crates/nu-lsp",
"crates/nu-pretty-hex",
"crates/nu-protocol",
"crates/nu-plugin",
"crates/nu_plugin_inc",
@ -47,8 +43,6 @@ members = [
"crates/nu_plugin_custom_values",
"crates/nu_plugin_formats",
"crates/nu-std",
"crates/nu-table",
"crates/nu-term-grid",
"crates/nu-utils",
]
@ -74,7 +68,6 @@ nu-table = { path = "./crates/nu-table", version = "0.88.2" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.88.2" }
nu-std = { path = "./crates/nu-std", version = "0.88.2" }
nu-utils = { path = "./crates/nu-utils", version = "0.88.2" }
nu-ansi-term = "0.49.0"
reedline = { version = "0.27.0", features = ["bashisms", "sqlite"] }
@ -173,7 +166,7 @@ bench = false
# To use a development version of a dependency please use a global override here
# changing versions in each sub-crate of the workspace is tedious
[patch.crates-io]
reedline = { git = "https://github.com/nushell/reedline.git", branch = "main" }
# reedline = { git = "https://github.com/nushell/reedline.git", branch = "main" }
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}
# uu_cp = { git = "https://github.com/uutils/coreutils.git", branch = "main" }

View File

@ -24,9 +24,13 @@ fn canonicalize_path(engine_state: &EngineState, path: &Path) -> PathBuf {
}
fn get_home_path(engine_state: &EngineState) -> PathBuf {
nu_path::home_dir()
.map(|path| canonicalize_path(engine_state, &path))
.unwrap_or_default()
let home_path = if let Some(path) = nu_path::home_dir() {
let canon_home_path = canonicalize_path(engine_state, &path);
canon_home_path
} else {
std::path::PathBuf::new()
};
home_path
}
// FIXME: All benchmarks live in this 1 file to speed up build times when benchmarking.

View File

@ -29,7 +29,7 @@ reedline = { version = "0.27.0", features = ["bashisms", "sqlite"] }
chrono = { default-features = false, features = ["std"], version = "0.4" }
crossterm = "0.27"
fancy-regex = "0.12"
fancy-regex = "0.11"
fuzzy-matcher = "0.3"
is_executable = "1.0"
log = "0.4"
@ -37,7 +37,7 @@ miette = { version = "5.10", features = ["fancy-no-backtrace"] }
once_cell = "1.18"
percent-encoding = "2"
pathdiff = "0.2"
sysinfo = "0.30"
sysinfo = "0.29"
unicode-segmentation = "1.10"
uuid = { version = "1.6.0", features = ["v4"] }
which = "5.0.0"

View File

@ -250,9 +250,7 @@ impl NuCompleter {
working_set.get_span_contents(previous_expr.0).to_vec();
// Completion for .nu files
if prev_expr_str == b"use"
|| prev_expr_str == b"overlay use"
|| prev_expr_str == b"source-env"
if prev_expr_str == b"use" || prev_expr_str == b"source-env"
{
let mut completer =
DotNuCompletion::new(self.engine_state.clone());
@ -354,7 +352,9 @@ impl NuCompleter {
if let Some(external_result) = self.external_completion(
block_id, &spans, offset, new_span,
) {
return external_result;
if !external_result.is_empty() {
return external_result;
}
}
}

View File

@ -22,10 +22,7 @@ fn complete_rec(
Some(base) if matches(base, &entry_name, options) => {
let partial = &partial[1..];
if !partial.is_empty() || isdir {
completions.extend(complete_rec(partial, &path, options, dir, isdir));
if entry_name.eq(base) {
break;
}
completions.extend(complete_rec(partial, &path, options, dir, isdir))
} else {
completions.push(path)
}

View File

@ -5,7 +5,7 @@ use nu_protocol::{
};
use reedline::Suggestion;
use std::{
path::{is_separator, Path, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR},
path::{is_separator, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR},
sync::Arc,
};
@ -91,21 +91,16 @@ impl Completer for DotNuCompletion {
// and transform them into suggestions
let output: Vec<Suggestion> = search_dirs
.into_iter()
.flat_map(|search_dir| {
let completions = file_path_completion(span, &partial, &search_dir, options);
completions
.flat_map(|it| {
file_path_completion(span, &partial, &it, options)
.into_iter()
.filter(move |it| {
.filter(|it| {
// Different base dir, so we list the .nu files or folders
if !is_current_folder {
it.1.ends_with(".nu") || it.1.ends_with(SEP)
} else {
// Lib dirs, so we filter only the .nu files or directory modules
if it.1.ends_with(SEP) {
Path::new(&search_dir).join(&it.1).join("mod.nu").exists()
} else {
it.1.ends_with(".nu")
}
// Lib dirs, so we filter only the .nu files
it.1.ends_with(".nu")
}
})
.map(move |x| Suggestion {

View File

@ -35,10 +35,6 @@ pub fn evaluate_commands(
let mut working_set = StateWorkingSet::new(engine_state);
let output = parse(&mut working_set, None, commands.item.as_bytes(), false);
if let Some(warning) = working_set.parse_warnings.first() {
report_error(&working_set, warning);
}
if let Some(err) = working_set.parse_errors.first() {
report_error(&working_set, err);

View File

@ -1,4 +1,3 @@
use crate::prompt_update::{POST_PROMPT_MARKER, PRE_PROMPT_MARKER};
#[cfg(windows)]
use nu_utils::enable_vt_processing;
use reedline::DefaultPrompt;
@ -12,7 +11,6 @@ use {
/// Nushell prompt definition
#[derive(Clone)]
pub struct NushellPrompt {
shell_integration: bool,
left_prompt_string: Option<String>,
right_prompt_string: Option<String>,
default_prompt_indicator: Option<String>,
@ -22,10 +20,15 @@ pub struct NushellPrompt {
render_right_prompt_on_last_line: bool,
}
impl Default for NushellPrompt {
fn default() -> Self {
NushellPrompt::new()
}
}
impl NushellPrompt {
pub fn new(shell_integration: bool) -> NushellPrompt {
pub fn new() -> NushellPrompt {
NushellPrompt {
shell_integration,
left_prompt_string: None,
right_prompt_string: None,
default_prompt_indicator: None,
@ -108,11 +111,7 @@ impl Prompt for NushellPrompt {
.to_string()
.replace('\n', "\r\n");
if self.shell_integration {
format!("{PRE_PROMPT_MARKER}{prompt}{POST_PROMPT_MARKER}").into()
} else {
prompt.into()
}
prompt.into()
}
}

View File

@ -7,6 +7,8 @@ use nu_protocol::{
Config, PipelineData, Value,
};
use reedline::Prompt;
use std::borrow::Cow;
use std::sync::Arc;
// Name of environment variable where the prompt could be stored
pub(crate) const PROMPT_COMMAND: &str = "PROMPT_COMMAND";
@ -26,8 +28,8 @@ pub(crate) const TRANSIENT_PROMPT_MULTILINE_INDICATOR: &str =
"TRANSIENT_PROMPT_MULTILINE_INDICATOR";
// According to Daniel Imms @Tyriar, we need to do these this way:
// <133 A><prompt><133 B><command><133 C><command output>
pub(crate) const PRE_PROMPT_MARKER: &str = "\x1b]133;A\x1b\\";
pub(crate) const POST_PROMPT_MARKER: &str = "\x1b]133;B\x1b\\";
const PRE_PROMPT_MARKER: &str = "\x1b]133;A\x1b\\";
const POST_PROMPT_MARKER: &str = "\x1b]133;B\x1b\\";
fn get_prompt_string(
prompt: &str,
@ -96,12 +98,12 @@ fn get_prompt_string(
})
}
pub(crate) fn update_prompt(
pub(crate) fn update_prompt<'prompt>(
config: &Config,
engine_state: &EngineState,
stack: &Stack,
nu_prompt: &mut NushellPrompt,
) {
nu_prompt: &'prompt mut NushellPrompt,
) -> &'prompt dyn Prompt {
let mut stack = stack.clone();
let left_prompt_string = get_prompt_string(PROMPT_COMMAND, config, engine_state, &mut stack);
@ -144,55 +146,125 @@ pub(crate) fn update_prompt(
(prompt_vi_insert_string, prompt_vi_normal_string),
config.render_right_prompt_on_last_line,
);
let ret_val = nu_prompt as &dyn Prompt;
trace!("update_prompt {}:{}:{}", file!(), line!(), column!());
ret_val
}
/// Construct the transient prompt based on the normal nu_prompt
pub(crate) fn make_transient_prompt(
struct TransientPrompt {
engine_state: Arc<EngineState>,
stack: Stack,
}
/// Try getting `$env.TRANSIENT_PROMPT_<X>`, and get `$env.PROMPT_<X>` if that fails
fn get_transient_prompt_string(
transient_prompt: &str,
prompt: &str,
config: &Config,
engine_state: &EngineState,
stack: &mut Stack,
nu_prompt: &NushellPrompt,
) -> Box<dyn Prompt> {
let mut nu_prompt = nu_prompt.clone();
if let Some(s) = get_prompt_string(TRANSIENT_PROMPT_COMMAND, config, engine_state, stack) {
nu_prompt.update_prompt_left(Some(s))
}
if let Some(s) = get_prompt_string(TRANSIENT_PROMPT_COMMAND_RIGHT, config, engine_state, stack)
{
nu_prompt.update_prompt_right(Some(s), config.render_right_prompt_on_last_line)
}
if let Some(s) = get_prompt_string(TRANSIENT_PROMPT_INDICATOR, config, engine_state, stack) {
nu_prompt.update_prompt_indicator(Some(s))
}
if let Some(s) = get_prompt_string(
TRANSIENT_PROMPT_INDICATOR_VI_INSERT,
config,
engine_state,
stack,
) {
nu_prompt.update_prompt_vi_insert(Some(s))
}
if let Some(s) = get_prompt_string(
TRANSIENT_PROMPT_INDICATOR_VI_NORMAL,
config,
engine_state,
stack,
) {
nu_prompt.update_prompt_vi_normal(Some(s))
}
if let Some(s) = get_prompt_string(
TRANSIENT_PROMPT_MULTILINE_INDICATOR,
config,
engine_state,
stack,
) {
nu_prompt.update_prompt_multiline(Some(s))
}
Box::new(nu_prompt)
) -> Option<String> {
get_prompt_string(transient_prompt, config, engine_state, stack)
.or_else(|| get_prompt_string(prompt, config, engine_state, stack))
}
impl Prompt for TransientPrompt {
fn render_prompt_left(&self) -> Cow<str> {
let mut nu_prompt = NushellPrompt::new();
let config = &self.engine_state.get_config().clone();
let mut stack = self.stack.clone();
nu_prompt.update_prompt_left(get_transient_prompt_string(
TRANSIENT_PROMPT_COMMAND,
PROMPT_COMMAND,
config,
&self.engine_state,
&mut stack,
));
nu_prompt.render_prompt_left().to_string().into()
}
fn render_prompt_right(&self) -> Cow<str> {
let mut nu_prompt = NushellPrompt::new();
let config = &self.engine_state.get_config().clone();
let mut stack = self.stack.clone();
nu_prompt.update_prompt_right(
get_transient_prompt_string(
TRANSIENT_PROMPT_COMMAND_RIGHT,
PROMPT_COMMAND_RIGHT,
config,
&self.engine_state,
&mut stack,
),
config.render_right_prompt_on_last_line,
);
nu_prompt.render_prompt_right().to_string().into()
}
fn render_prompt_indicator(&self, prompt_mode: reedline::PromptEditMode) -> Cow<str> {
let mut nu_prompt = NushellPrompt::new();
let config = &self.engine_state.get_config().clone();
let mut stack = self.stack.clone();
nu_prompt.update_prompt_indicator(get_transient_prompt_string(
TRANSIENT_PROMPT_INDICATOR,
PROMPT_INDICATOR,
config,
&self.engine_state,
&mut stack,
));
nu_prompt.update_prompt_vi_insert(get_transient_prompt_string(
TRANSIENT_PROMPT_INDICATOR_VI_INSERT,
PROMPT_INDICATOR_VI_INSERT,
config,
&self.engine_state,
&mut stack,
));
nu_prompt.update_prompt_vi_normal(get_transient_prompt_string(
TRANSIENT_PROMPT_INDICATOR_VI_NORMAL,
PROMPT_INDICATOR_VI_NORMAL,
config,
&self.engine_state,
&mut stack,
));
nu_prompt
.render_prompt_indicator(prompt_mode)
.to_string()
.into()
}
fn render_prompt_multiline_indicator(&self) -> Cow<str> {
let mut nu_prompt = NushellPrompt::new();
let config = &self.engine_state.get_config().clone();
let mut stack = self.stack.clone();
nu_prompt.update_prompt_multiline(get_transient_prompt_string(
TRANSIENT_PROMPT_MULTILINE_INDICATOR,
PROMPT_MULTILINE_INDICATOR,
config,
&self.engine_state,
&mut stack,
));
nu_prompt
.render_prompt_multiline_indicator()
.to_string()
.into()
}
fn render_prompt_history_search_indicator(
&self,
history_search: reedline::PromptHistorySearch,
) -> Cow<str> {
NushellPrompt::new()
.render_prompt_history_search_indicator(history_search)
.to_string()
.into()
}
}
/// Construct the transient prompt
pub(crate) fn transient_prompt(engine_state: Arc<EngineState>, stack: &Stack) -> Box<dyn Prompt> {
Box::new(TransientPrompt {
engine_state,
stack: stack.clone(),
})
}

View File

@ -32,7 +32,7 @@ use std::{
sync::atomic::Ordering,
time::Instant,
};
use sysinfo::System;
use sysinfo::SystemExt;
// According to Daniel Imms @Tyriar, we need to do these this way:
// <133 A><prompt><133 B><command><133 C><command output>
@ -54,8 +54,7 @@ pub fn evaluate_repl(
) -> Result<()> {
use nu_cmd_base::hook;
use reedline::Signal;
let config = engine_state.get_config();
let use_color = config.use_ansi_coloring;
let use_color = engine_state.get_config().use_ansi_coloring;
// Guard against invocation without a connected terminal.
// reedline / crossterm event polling will fail without a connected tty
@ -69,7 +68,7 @@ pub fn evaluate_repl(
let mut entry_num = 0;
let mut nu_prompt = NushellPrompt::new(config.shell_integration);
let mut nu_prompt = NushellPrompt::new();
let start_time = std::time::Instant::now();
// Translate environment variables from Strings to Values
@ -135,6 +134,17 @@ pub fn evaluate_repl(
use_color,
);
start_time = std::time::Instant::now();
let sys = sysinfo::System::new();
perf(
"get sysinfo",
start_time,
file!(),
line!(),
column!(),
use_color,
);
if let Some(s) = prerun_command {
eval_source(
engine_state,
@ -202,6 +212,20 @@ pub fn evaluate_repl(
use_color,
);
start_time = std::time::Instant::now();
// Reset the SIGQUIT handler
if let Some(sig_quit) = engine_state.get_sig_quit() {
sig_quit.store(false, Ordering::SeqCst);
}
perf(
"reset sig_quit",
start_time,
file!(),
line!(),
column!(),
use_color,
);
start_time = std::time::Instant::now();
let config = engine_state.get_config();
@ -243,7 +267,11 @@ pub fn evaluate_repl(
.with_quick_completions(config.quick_completions)
.with_partial_completions(config.partial_completions)
.with_ansi_colors(config.use_ansi_coloring)
.with_cursor_config(cursor_config);
.with_cursor_config(cursor_config)
.with_transient_prompt(prompt_update::transient_prompt(
engine_reference.clone(),
stack,
));
perf(
"reedline builder",
start_time,
@ -396,9 +424,7 @@ pub fn evaluate_repl(
start_time = std::time::Instant::now();
let config = &engine_state.get_config().clone();
prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt);
let transient_prompt =
prompt_update::make_transient_prompt(config, engine_state, stack, &nu_prompt);
let prompt = prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt);
perf(
"update_prompt",
start_time,
@ -411,13 +437,12 @@ pub fn evaluate_repl(
entry_num += 1;
start_time = std::time::Instant::now();
line_editor = line_editor.with_transient_prompt(transient_prompt);
let input = line_editor.read_line(&nu_prompt);
let input = line_editor.read_line(prompt);
let shell_integration = config.shell_integration;
match input {
Ok(Signal::Success(s)) => {
let hostname = System::host_name();
let hostname = sys.host_name();
let history_supports_meta =
matches!(config.history_file_format, HistoryFileFormat::Sqlite);
if history_supports_meta && !s.is_empty() && line_editor.has_last_command_context()
@ -739,7 +764,7 @@ fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> Option<SetCursorSty
}
}
fn get_command_finished_marker(stack: &Stack, engine_state: &EngineState) -> String {
pub fn get_command_finished_marker(stack: &Stack, engine_state: &EngineState) -> String {
let exit_code = stack
.get_env_var(engine_state, "LAST_EXIT_CODE")
.and_then(|e| e.as_i64().ok());

View File

@ -329,6 +329,7 @@ fn find_matching_block_end_in_expr(
Expr::ImportPattern(_) => None,
Expr::Overlay(_) => None,
Expr::Signature(_) => None,
Expr::MatchPattern(_) => None,
Expr::MatchBlock(_) => None,
Expr::Nothing => None,
Expr::Garbage => None,
@ -385,7 +386,6 @@ fn find_matching_block_end_in_expr(
Argument::Named((_, _, opt_expr)) => opt_expr.as_ref(),
Argument::Positional(inner_expr) => Some(inner_expr),
Argument::Unknown(inner_expr) => Some(inner_expr),
Argument::Spread(inner_expr) => Some(inner_expr),
};
if let Some(inner_expr) = opt_expr {

View File

@ -220,10 +220,6 @@ pub fn eval_source(
source,
false,
);
if let Some(warning) = working_set.parse_warnings.first() {
report_error(&working_set, warning);
}
if let Some(err) = working_set.parse_errors.first() {
set_last_exit_code(stack, 1);
report_error(&working_set, err);

View File

@ -91,7 +91,7 @@ fn variables_dollar_sign_with_varialblecompletion() {
let target_dir = "$ ";
let suggestions = completer.complete(target_dir, target_dir.len());
assert_eq!(8, suggestions.len());
assert_eq!(7, suggestions.len());
}
#[rstest]
@ -144,34 +144,15 @@ fn dotnu_completions() {
let completion_str = "source-env ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(2, suggestions.len());
assert_eq!(1, suggestions.len());
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
#[cfg(windows)]
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
#[cfg(not(windows))]
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
// Test use completion
let completion_str = "use ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(2, suggestions.len());
assert_eq!(1, suggestions.len());
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
#[cfg(windows)]
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
#[cfg(not(windows))]
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
// Test overlay use completion
let completion_str = "overlay use ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(2, suggestions.len());
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
#[cfg(windows)]
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
#[cfg(not(windows))]
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
}
#[test]
@ -227,7 +208,6 @@ fn file_completions() {
let expected_paths: Vec<String> = vec![
folder(dir.join("another")),
file(dir.join("custom_completion.nu")),
folder(dir.join("directory_completion")),
file(dir.join("nushell")),
folder(dir.join("test_a")),
folder(dir.join("test_b")),
@ -343,7 +323,6 @@ fn command_ls_with_filecompletion() {
let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
@ -354,7 +333,6 @@ fn command_ls_with_filecompletion() {
let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
@ -377,7 +355,6 @@ fn command_open_with_filecompletion() {
let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
@ -388,7 +365,6 @@ fn command_open_with_filecompletion() {
let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
@ -412,7 +388,6 @@ fn command_rm_with_globcompletion() {
let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
@ -423,7 +398,6 @@ fn command_rm_with_globcompletion() {
let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
@ -447,7 +421,6 @@ fn command_cp_with_globcompletion() {
let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
@ -458,7 +431,6 @@ fn command_cp_with_globcompletion() {
let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
@ -482,7 +454,6 @@ fn command_save_with_filecompletion() {
let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
@ -493,7 +464,6 @@ fn command_save_with_filecompletion() {
let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
@ -517,7 +487,6 @@ fn command_touch_with_filecompletion() {
let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
@ -528,7 +497,6 @@ fn command_touch_with_filecompletion() {
let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
@ -552,7 +520,6 @@ fn command_watch_with_filecompletion() {
let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
@ -563,7 +530,6 @@ fn command_watch_with_filecompletion() {
let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
@ -659,7 +625,6 @@ fn folder_with_directorycompletions() {
// Create the expected values
let expected_paths: Vec<String> = vec![
folder(dir.join("another")),
folder(dir.join("directory_completion")),
folder(dir.join("test_a")),
folder(dir.join("test_b")),
folder(dir.join(".hidden_folder")),
@ -874,7 +839,6 @@ fn unknown_command_completion() {
let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
@ -885,7 +849,6 @@ fn unknown_command_completion() {
let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
@ -936,7 +899,6 @@ fn filecompletions_triggers_after_cursor() {
let expected_paths: Vec<String> = vec![
"another\\".to_string(),
"custom_completion.nu".to_string(),
"directory_completion\\".to_string(),
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
@ -947,7 +909,6 @@ fn filecompletions_triggers_after_cursor() {
let expected_paths: Vec<String> = vec![
"another/".to_string(),
"custom_completion.nu".to_string(),
"directory_completion/".to_string(),
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),

View File

@ -26,9 +26,9 @@ num = { version = "0.4", optional = true }
serde = { version = "1.0", features = ["derive"] }
sqlparser = { version = "0.39", optional = true }
polars-io = { version = "0.35", features = ["avro"], optional = true }
polars-arrow = { version = "0.35", optional = true }
polars-ops = { version = "0.35", optional = true }
polars-plan = { version = "0.35", optional = true }
polars-arrow = "0.35"
polars-ops = "0.35"
polars-plan = "0.35"
[dependencies.polars]
features = [
@ -65,7 +65,7 @@ optional = true
version = "0.35"
[features]
dataframe = ["num", "polars", "polars-io", "polars-arrow", "polars-ops", "polars-plan", "sqlparser"]
dataframe = ["num", "polars", "polars-io", "sqlparser"]
default = []
[dev-dependencies]

View File

@ -874,7 +874,7 @@ fn series_to_values(
.iter()
.map(|field| field.name.to_string())
.collect();
let record = Record::from_raw_cols_vals(cols, vals?);
let record = Record { cols, vals: vals? };
Ok(Value::record(record, span))
})
.collect();
@ -982,7 +982,10 @@ fn any_value_to_value(any_value: &AnyValue, span: Span) -> Result<Value, ShellEr
.map(|f| f.name().to_string())
.collect();
Ok(Value::Record {
val: Record::from_raw_cols_vals(fields, values?),
val: Record {
cols: fields,
vals: values?,
},
internal_span: span,
})
}
@ -1056,7 +1059,6 @@ fn time_from_midnight(nanos: i64, span: Span) -> Result<Value, ShellError> {
#[cfg(test)]
mod tests {
use indexmap::indexmap;
use nu_protocol::record;
use polars::export::arrow::array::{BooleanArray, PrimitiveArray};
use polars::prelude::Field;
use polars_io::prelude::StructArray;
@ -1247,10 +1249,13 @@ mod tests {
Field::new(field_name_1, DataType::Boolean),
];
let test_owned_struct = AnyValue::StructOwned(Box::new((values, fields.clone())));
let comparison_owned_record = Value::test_record(record!(
field_name_0 => Value::int(1, span),
field_name_1 => Value::bool(true, span),
));
let comparison_owned_record = Value::record(
Record {
cols: vec![field_name_0.to_owned(), field_name_1.to_owned()],
vals: vec![Value::int(1, span), Value::bool(true, span)],
},
span,
);
assert_eq!(
any_value_to_value(&test_owned_struct, span)?,
comparison_owned_record.clone()

View File

@ -158,10 +158,7 @@ impl NuDataFrame {
.map(|i| format!("{i}"))
.collect::<Vec<String>>();
conversion::insert_record(
&mut column_values,
Record::from_raw_cols_vals(cols, vals),
)?
conversion::insert_record(&mut column_values, Record { cols, vals })?
}
Value::Record { val: record, .. } => {
conversion::insert_record(&mut column_values, record)?

View File

@ -24,7 +24,7 @@ heck = "0.4.1"
num-traits = "0.2"
ahash = "0.8.3"
nu-ansi-term = "0.49.0"
fancy-regex = "0.12.0"
fancy-regex = "0.11.0"
rust-embed = "8.0.0"
serde = "1.0.164"
nu-pretty-hex = { version = "0.88.2", path = "../nu-pretty-hex" }

View File

@ -65,7 +65,7 @@ pub fn add_extra_command_context(mut engine_state: EngineState) -> EngineState {
bind_command!(platform::ansi::Gradient);
bind_command!(
strings::format::FormatPattern,
strings::format::Format,
strings::encode_decode::EncodeHex,
strings::encode_decode::DecodeHex,
strings::str_::case::Str,

View File

@ -10,15 +10,15 @@ use nu_protocol::{
};
#[derive(Clone)]
pub struct FormatPattern;
pub struct Format;
impl Command for FormatPattern {
impl Command for Format {
fn name(&self) -> &str {
"format pattern"
"format"
}
fn signature(&self) -> Signature {
Signature::build("format pattern")
Signature::build("format")
.input_output_types(vec![
(Type::Table(vec![]), Type::List(Box::new(Type::String))),
(Type::Record(vec![]), Type::Any),
@ -80,12 +80,12 @@ impl Command for FormatPattern {
vec![
Example {
description: "Print filenames with their sizes",
example: "ls | format pattern '{name}: {size}'",
example: "ls | format '{name}: {size}'",
result: None,
},
Example {
description: "Print elements from some columns of a table",
example: "[[col1, col2]; [v1, v2] [v3, v4]] | format pattern '{col2}'",
example: "[[col1, col2]; [v1, v2] [v3, v4]] | format '{col2}'",
result: Some(Value::list(
vec![Value::test_string("v2"), Value::test_string("v4")],
Span::test_data(),
@ -318,8 +318,8 @@ fn format_record(
mod test {
#[test]
fn test_examples() {
use super::FormatPattern;
use super::Format;
use crate::test_examples;
test_examples(FormatPattern {})
test_examples(Format {})
}
}

View File

@ -1,3 +1,3 @@
mod command;
pub(crate) use command::FormatPattern;
pub(crate) use command::Format;

View File

@ -18,12 +18,12 @@ nu-protocol = { path = "../nu-protocol", version = "0.88.2" }
nu-utils = { path = "../nu-utils", version = "0.88.2" }
nu-ansi-term = "0.49.0"
fancy-regex = "0.12"
itertools = "0.12"
shadow-rs = { version = "0.26", default-features = false }
fancy-regex = "0.11"
itertools = "0.11"
shadow-rs = { version = "0.24", default-features = false }
[build-dependencies]
shadow-rs = { version = "0.26", default-features = false }
shadow-rs = { version = "0.24", default-features = false }
[features]
mimalloc = []

View File

@ -290,6 +290,7 @@ fn describe_value(
| Value::Date { .. }
| Value::Range { .. }
| Value::String { .. }
| Value::MatchPattern { .. }
| Value::Nothing { .. } => Value::record(
record!(
"type" => Value::string(value.get_type().to_string(), head),

View File

@ -278,6 +278,9 @@ impl<'a> std::fmt::Debug for DebuggableValue<'a> {
let rec = val.collect().map_err(|_| std::fmt::Error)?;
write!(f, "LazyRecord({:?})", DebuggableValue(&rec))
}
Value::MatchPattern { val, .. } => {
write!(f, "MatchPattern({:?})", val)
}
}
}
}

View File

@ -92,7 +92,7 @@ fn color_string_to_nustyle(color_string: String) -> Style {
mod tests {
use super::*;
use nu_ansi_term::{Color, Style};
use nu_protocol::{record, Span, Value};
use nu_protocol::{Span, Value};
#[test]
fn test_color_string_to_nustyle_empty_string() {
@ -120,10 +120,13 @@ mod tests {
#[test]
fn test_get_style_from_value() {
// Test case 1: all values are valid
let record = record! {
"bg" => Value::test_string("red"),
"fg" => Value::test_string("blue"),
"attr" => Value::test_string("bold"),
let record = Record {
cols: vec!["bg".to_string(), "fg".to_string(), "attr".to_string()],
vals: vec![
Value::string("red", Span::unknown()),
Value::string("blue", Span::unknown()),
Value::string("bold", Span::unknown()),
],
};
let expected_style = NuStyle {
bg: Some("red".to_string()),
@ -133,15 +136,19 @@ mod tests {
assert_eq!(get_style_from_value(&record), Some(expected_style));
// Test case 2: no values are valid
let record = record! {
"invalid" => Value::nothing(Span::test_data()),
let record = Record {
cols: vec!["invalid".to_string()],
vals: vec![Value::nothing(Span::unknown())],
};
assert_eq!(get_style_from_value(&record), None);
// Test case 3: some values are valid
let record = record! {
"bg" => Value::test_string("green"),
"invalid" => Value::nothing(Span::unknown()),
let record = Record {
cols: vec!["bg".to_string(), "invalid".to_string()],
vals: vec![
Value::string("green", Span::unknown()),
Value::nothing(Span::unknown()),
],
};
let expected_style = NuStyle {
bg: Some("green".to_string()),

View File

@ -131,7 +131,8 @@ impl<'a> StyleComputer<'a> {
Value::Closure { .. }
| Value::CustomValue { .. }
| Value::Error { .. }
| Value::LazyRecord { .. } => TextStyle::basic_left(),
| Value::LazyRecord { .. }
| Value::MatchPattern { .. } => TextStyle::basic_left(),
}
}

View File

@ -42,7 +42,7 @@ dialoguer = { default-features = false, features = ["fuzzy-select"], version = "
digest = { default-features = false, version = "0.10" }
dtparse = "2.0"
encoding_rs = "0.8"
fancy-regex = "0.12"
fancy-regex = "0.11"
filesize = "0.2"
filetime = "0.2"
fs_extra = "1.3"
@ -50,9 +50,9 @@ htmlescape = "0.3"
human-date-parser = "0.1.1"
indexmap = "2.1"
indicatif = "0.17"
itertools = "0.12"
itertools = "0.11"
log = "0.4"
lscolors = { version = "0.16", default-features = false, features = ["nu-ansi-term"] }
lscolors = { version = "0.15", default-features = false, features = ["nu-ansi-term"] }
md5 = { package = "md-5", version = "0.10" }
miette = { version = "5.10", features = ["fancy-no-backtrace"] }
mime = "0.3"
@ -80,7 +80,7 @@ serde_json = "1.0"
serde_urlencoded = "0.7"
serde_yaml = "0.9"
sha2 = "0.10"
sysinfo = "0.30"
sysinfo = "0.29"
tabled = { version = "0.14.0", features = ["color"], default-features = false }
terminal_size = "0.3"
titlecase = "2.0"
@ -104,9 +104,9 @@ winreg = "0.52"
[target.'cfg(unix)'.dependencies]
libc = "0.2"
umask = "2.1"
nix = { version = "0.27", default-features = false, features = ["user", "resource"] }
nix = { version = "0.27", default-features = false, features = ["user"] }
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
[target.'cfg(all(unix, not(target_os = "macos"), not(target_os = "android"), not(target_os = "ios")))'.dependencies]
procfs = "0.16.0"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
@ -122,7 +122,7 @@ features = [
"Win32_Security",
"Win32_System_Threading",
]
version = "0.52"
version = "0.48"
[features]
plugin = ["nu-parser/plugin"]

View File

@ -1,4 +1,4 @@
use nu_engine::{eval_expression, CallExt};
use nu_engine::eval_expression;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
@ -48,7 +48,8 @@ impl Command for BytesBuild {
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let mut output = vec![];
for val in call.rest_iter_flattened(0, |expr| eval_expression(engine_state, stack, expr))? {
for expr in call.positional_iter() {
let val = eval_expression(engine_state, stack, expr)?;
match val {
Value::Binary { mut val, .. } => output.append(&mut val),
// Explicitly propagate errors instead of dropping them.

View File

@ -105,13 +105,6 @@ impl Command for BytesRemove {
vec![0x10, 0xAA, 0x10, 0xBB, 0xCC, 0xAA],
)),
},
Example {
description: "Remove find binary from end not found",
example: "0x[10 AA 10 BB CC AA 10] | bytes remove --end 0x[11]",
result: Some(Value::test_binary (
vec![0x10, 0xAA, 0x10, 0xBB, 0xCC, 0xAA, 0x10],
)),
},
Example {
description: "Remove all occurrences of find binary in table",
example: "[[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes remove 0x[11] ColA ColC",
@ -166,11 +159,8 @@ fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
}
// append the remaining thing to result, this can be happening when
// we have something to remove and remove_all is False.
// check if the left is positive, if it is not, we don't need to append anything.
if left > 0 {
let mut remain = input[..left as usize].iter().copied().rev().collect();
result.append(&mut remain);
}
let mut remain = input[..left as usize].iter().copied().rev().collect();
result.append(&mut remain);
result = result.into_iter().rev().collect();
Value::binary(result, span)
} else {

View File

@ -258,16 +258,16 @@ fn histogram_impl(
result.push((
count, // attach count first for easily sorting.
Value::record(
Record::from_raw_cols_vals(
result_cols.clone(),
vec![
Record {
cols: result_cols.clone(),
vals: vec![
val.into_value(),
Value::int(count, span),
Value::float(quantile, span),
Value::string(percentage, span),
Value::string(freq, span),
],
),
},
span,
),
));

View File

@ -260,6 +260,7 @@ fn nu_value_to_string(value: Value, separator: &str) -> String {
Value::Binary { val, .. } => format!("{val:?}"),
Value::CellPath { val, .. } => val.to_string(),
Value::CustomValue { val, .. } => val.value_string(),
Value::MatchPattern { val, .. } => format!("{:?}", val),
}
}

View File

@ -499,7 +499,13 @@ pub fn convert_sqlite_row_to_nu_value(row: &Row, span: Span, column_names: Vec<S
vals.push(val);
}
Value::record(Record::from_raw_cols_vals(column_names, vals), span)
Value::record(
Record {
cols: column_names,
vals,
},
span,
)
}
pub fn convert_sqlite_value_to_nu_value(value: ValueRef, span: Span) -> Value {

View File

@ -191,24 +191,6 @@ fn get_arguments(engine_state: &EngineState, stack: &mut Stack, call: Call) -> V
let arg_value_name_span_start = evaled_span.start as i64;
let arg_value_name_span_end = evaled_span.end as i64;
let record = record! {
"arg_type" => Value::string(arg_type, span),
"name" => Value::string(arg_value_name, inner_expr.span),
"type" => Value::string(arg_value_type, span),
"span_start" => Value::int(arg_value_name_span_start, span),
"span_end" => Value::int(arg_value_name_span_end, span),
};
arg_value.push(Value::record(record, inner_expr.span));
}
Argument::Spread(inner_expr) => {
let arg_type = "spread";
let evaluated_expression = get_expression_as_value(engine_state, stack, inner_expr);
let arg_value_name = debug_string_without_formatting(&evaluated_expression);
let arg_value_type = &evaluated_expression.get_type().to_string();
let evaled_span = evaluated_expression.span();
let arg_value_name_span_start = evaled_span.start as i64;
let arg_value_name_span_end = evaled_span.end as i64;
let record = record! {
"arg_type" => Value::string(arg_type, span),
"name" => Value::string(arg_value_name, inner_expr.span),
@ -277,5 +259,6 @@ pub fn debug_string_without_formatting(value: &Value) -> String {
Value::Binary { val, .. } => format!("{val:?}"),
Value::CellPath { val, .. } => val.to_string(),
Value::CustomValue { val, .. } => val.value_string(),
Value::MatchPattern { val, .. } => format!("{:?}", val),
}
}

View File

@ -4,7 +4,7 @@ use nu_protocol::{
record, Category, Example, IntoPipelineData, LazyRecord, PipelineData, Record, ShellError,
Signature, Span, Type, Value,
};
use sysinfo::{MemoryRefreshKind, Pid, ProcessRefreshKind, RefreshKind, System};
use sysinfo::{Pid, PidExt, ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt};
const ENV_PATH_SEPARATOR_CHAR: char = {
#[cfg(target_family = "windows")]
{
@ -98,9 +98,8 @@ impl LazySystemInfoRecord {
}
"system" => {
// only get information requested
let system_opt = SystemOpt::from((system_option, || {
RefreshKind::new().with_memory(MemoryRefreshKind::everything())
}));
let system_opt =
SystemOpt::from((system_option, || RefreshKind::new().with_memory()));
let system = system_opt.get_system();
@ -136,66 +135,53 @@ impl LazySystemInfoRecord {
"virtual_memory" => Value::filesize(p.virtual_memory() as i64, self.span),
"status" => Value::string(p.status().to_string(), self.span),
"root" => {
if let Some(path) = p.exe().and_then(|p| p.parent()) {
Value::string(path.to_string_lossy().to_string(), self.span)
if let Some(filename) = p.exe().parent() {
Value::string(filename.to_string_lossy().to_string(), self.span)
} else {
Value::nothing(self.span)
}
},
"cwd" => {
if let Some(path) = p.cwd() {
Value::string(path.to_string_lossy().to_string(), self.span)
}else{
Value::nothing(self.span)
}
},
"exe_path" => {
if let Some(path)= p.exe() {
Value::string(path.to_string_lossy().to_string(), self.span)
}else{
Value::nothing(self.span)
}
},
"cwd" => Value::string(p.cwd().to_string_lossy().to_string(), self.span),
"exe_path" => Value::string(p.exe().to_string_lossy().to_string(), self.span),
"command" => Value::string(p.cmd().join(" "), self.span),
"name" => Value::string(p.name().to_string(), self.span),
"environment" => {
let mut env_rec = Record::new();
for val in p.environ() {
if let Some((key, value)) = val.split_once('=') {
let is_env_var_a_list = {
{
#[cfg(target_family = "windows")]
"environment" => {
let mut env_rec = Record::new();
for val in p.environ() {
if let Some((key, value)) = val.split_once('=') {
let is_env_var_a_list = {
{
key == "Path" || key == "PATHEXT" || key == "PSMODULEPATH" || key == "PSModulePath"
}
#[cfg(not(target_family = "windows"))]
{
key == "PATH" || key == "DYLD_FALLBACK_LIBRARY_PATH"
#[cfg(target_family = "windows")]
{
key == "Path" || key == "PATHEXT" || key == "PSMODULEPATH" || key == "PSModulePath"
}
#[cfg(not(target_family = "windows"))]
{
key == "PATH" || key == "DYLD_FALLBACK_LIBRARY_PATH"
}
}
};
if is_env_var_a_list {
let items = value.split(ENV_PATH_SEPARATOR_CHAR).map(|r| Value::string(r.to_string(), self.span)).collect::<Vec<_>>();
env_rec.push(key.to_string(), Value::list(items, self.span));
} else if key == "LS_COLORS" { // LS_COLORS is a special case, it's a colon separated list of key=value pairs
let items = value.split(':').map(|r| Value::string(r.to_string(), self.span)).collect::<Vec<_>>();
env_rec.push(key.to_string(), Value::list(items, self.span));
} else {
env_rec.push(key.to_string(), Value::string(value.to_string(), self.span));
}
};
if is_env_var_a_list {
let items = value.split(ENV_PATH_SEPARATOR_CHAR).map(|r| Value::string(r.to_string(), self.span)).collect::<Vec<_>>();
env_rec.push(key.to_string(), Value::list(items, self.span));
} else if key == "LS_COLORS" { // LS_COLORS is a special case, it's a colon separated list of key=value pairs
let items = value.split(':').map(|r| Value::string(r.to_string(), self.span)).collect::<Vec<_>>();
env_rec.push(key.to_string(), Value::list(items, self.span));
} else {
env_rec.push(key.to_string(), Value::string(value.to_string(), self.span));
}
}
}
Value::record(env_rec, self.span)
},
Value::record(env_rec, self.span)
},
},
self.span,
))
} else {
// If we can't get the process information, just return the system information
// only get information requested
let system_opt = SystemOpt::from((system_option, || {
RefreshKind::new().with_memory(MemoryRefreshKind::everything())
}));
let system_opt =
SystemOpt::from((system_option, || RefreshKind::new().with_memory()));
let system = system_opt.get_system();
Ok(Value::record(
@ -242,7 +228,7 @@ impl<'a> LazyRecord<'a> for LazySystemInfoRecord {
.without_disk_usage()
.without_user(),
)
.with_memory(MemoryRefreshKind::everything());
.with_memory();
// only get information requested
let system = System::new_with_specifics(rk);

View File

@ -86,12 +86,6 @@ impl Command for Metadata {
PipelineMetadata {
data_source: DataSource::HtmlThemes,
} => record.push("source", Value::string("into html --list", head)),
PipelineMetadata {
data_source: DataSource::FilePath(path),
} => record.push(
"source",
Value::string(path.to_string_lossy().to_string(), head),
),
}
}
@ -139,12 +133,6 @@ fn build_metadata_record(arg: &Value, metadata: Option<&PipelineMetadata>, head:
PipelineMetadata {
data_source: DataSource::HtmlThemes,
} => record.push("source", Value::string("into html --list", head)),
PipelineMetadata {
data_source: DataSource::FilePath(path),
} => record.push(
"source",
Value::string(path.to_string_lossy().to_string(), head),
),
}
}

View File

@ -191,7 +191,6 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
StrSubstring,
StrTrim,
StrUpcase,
Format,
FormatDate,
FormatDuration,
FormatFilesize,
@ -233,9 +232,6 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
Whoami,
};
#[cfg(unix)]
bind_command! { ULimit };
// Date
bind_command! {
Date,

View File

@ -66,17 +66,16 @@ fn is_root_impl() -> bool {
System::Threading::{GetCurrentProcess, OpenProcessToken},
};
let mut handle = HANDLE::default();
let mut elevated = false;
// Checks whether the access token associated with the current process has elevated privileges.
// SAFETY: `elevated` only touched by safe code.
// `handle` lives long enough, initialized, mutated, used and closed with validity check.
// `handle` lives long enough, initialized, mutated as out param, used, closed with validity check.
// `elevation` only read on success and passed with correct `size`.
unsafe {
let mut handle = HANDLE::default();
// Opens the access token associated with the current process.
if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut handle).is_ok() {
if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut handle).as_bool() {
let mut elevation = TOKEN_ELEVATION::default();
let mut size = std::mem::size_of::<TOKEN_ELEVATION>() as u32;
@ -90,7 +89,7 @@ fn is_root_impl() -> bool {
size,
&mut size,
)
.is_ok()
.as_bool()
{
// Whether the token has elevated privileges.
// Safe to read as `GetTokenInformation` will not write outside `elevation` and it succeeded
@ -101,7 +100,7 @@ fn is_root_impl() -> bool {
if !handle.is_invalid() {
// Closes the object handle.
let _ = CloseHandle(handle);
CloseHandle(handle);
}
}

View File

@ -229,16 +229,7 @@ fn have_permission(dir: impl AsRef<Path>) -> PermissionResult<'static> {
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
fn any_group(_current_user_gid: gid_t, owner_group: u32) -> bool {
use crate::filesystem::util::users;
let Some(user_groups) = users::current_user_groups() else {
return false;
};
user_groups.iter().any(|gid| gid.as_raw() == owner_group)
}
#[cfg(all(unix, not(any(target_os = "linux", target_os = "android"))))]
#[cfg(unix)]
fn any_group(current_user_gid: gid_t, owner_group: u32) -> bool {
use crate::filesystem::util::users;

View File

@ -1,11 +1,10 @@
use nu_engine::{current_dir, eval_block, CallExt};
use nu_path::expand_to_real_path;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::util::BufferedReader;
use nu_protocol::{
Category, DataSource, Example, IntoInterruptiblePipelineData, PipelineData, PipelineMetadata,
RawStream, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
Category, Example, IntoInterruptiblePipelineData, PipelineData, RawStream, ShellError,
Signature, Spanned, SyntaxShape, Type, Value,
};
use std::io::BufReader;
@ -158,7 +157,6 @@ impl Command for Open {
};
let buf_reader = BufReader::new(file);
let real_path = expand_to_real_path(path);
let file_contents = PipelineData::ExternalStream {
stdout: Some(RawStream::new(
@ -170,9 +168,7 @@ impl Command for Open {
stderr: None,
exit_code: None,
span: call_span,
metadata: Some(PipelineMetadata {
data_source: DataSource::FilePath(real_path),
}),
metadata: None,
trim_end_newline: false,
};
let exts_opt: Option<Vec<String>> = if raw {

View File

@ -381,23 +381,11 @@ fn rm(
{
unreachable!()
}
} else if metadata.is_symlink() {
// In Windows, symlink pointing to a directory can be removed using
// std::fs::remove_dir instead of std::fs::remove_file.
#[cfg(windows)]
{
f.metadata().and_then(|metadata| {
if metadata.is_dir() {
std::fs::remove_dir(&f)
} else {
std::fs::remove_file(&f)
}
})
}
#[cfg(not(windows))]
std::fs::remove_file(&f)
} else if metadata.is_file() || is_socket || is_fifo {
} else if metadata.is_file()
|| is_socket
|| is_fifo
|| metadata.file_type().is_symlink()
{
std::fs::remove_file(&f)
} else {
std::fs::remove_dir_all(&f)

View File

@ -4,8 +4,8 @@ use nu_path::expand_path_with;
use nu_protocol::ast::{Call, Expr, Expression};
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, DataSource, Example, PipelineData, PipelineMetadata, RawStream, ShellError,
Signature, Span, Spanned, SyntaxShape, Type, Value,
Category, Example, PipelineData, RawStream, ShellError, Signature, Span, Spanned, SyntaxShape,
Type, Value,
};
use std::fs::File;
use std::io::Write;
@ -149,42 +149,9 @@ impl Command for Save {
res
}
}
PipelineData::ListStream(ls, pipeline_metadata)
PipelineData::ListStream(ls, _)
if raw || prepare_path(&path, append, force)?.0.extension().is_none() =>
{
if let Some(PipelineMetadata {
data_source: DataSource::FilePath(input_path),
}) = pipeline_metadata
{
if path.item == input_path {
return Err(ShellError::GenericError {
error: "pipeline input and output are same file".into(),
msg: format!(
"can't save output to '{}' while it's being reading",
path.item.display()
),
span: Some(path.span),
help: Some("you should change output path".into()),
inner: vec![],
});
}
if let Some(ref err_path) = stderr_path {
if err_path.item == input_path {
return Err(ShellError::GenericError {
error: "pipeline input and stderr are same file".into(),
msg: format!(
"can't save stderr to '{}' while it's being reading",
err_path.item.display()
),
span: Some(err_path.span),
help: Some("you should change stderr path".into()),
inner: vec![],
});
}
}
}
let (mut file, _) = get_files(
&path,
stderr_path.as_ref(),
@ -373,7 +340,10 @@ fn prepare_path(
fn open_file(path: &Path, span: Span, append: bool) -> Result<File, ShellError> {
let file = match (append, path.exists()) {
(true, true) => std::fs::OpenOptions::new().append(true).open(path),
(true, true) => std::fs::OpenOptions::new()
.write(true)
.append(true)
.open(path),
_ => std::fs::File::create(path),
};

View File

@ -165,8 +165,9 @@ pub fn is_older(src: &Path, dst: &Path) -> Option<bool> {
#[cfg(unix)]
pub mod users {
use libc::{gid_t, uid_t};
use libc::{c_int, gid_t, uid_t};
use nix::unistd::{Gid, Group, Uid, User};
use std::ffi::CString;
pub fn get_user_by_uid(uid: uid_t) -> Option<User> {
User::from_uid(Uid::from_raw(uid)).ok().flatten()
@ -184,7 +185,6 @@ pub mod users {
Gid::current().as_raw()
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn get_current_username() -> Option<String> {
User::from_uid(Uid::current())
.ok()
@ -192,30 +192,6 @@ pub mod users {
.map(|user| user.name)
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn current_user_groups() -> Option<Vec<Gid>> {
// SAFETY:
// if first arg is 0 then it ignores second argument and returns number of groups present for given user.
let ngroups = unsafe { libc::getgroups(0, core::ptr::null::<gid_t> as *mut _) };
let mut buff: Vec<gid_t> = vec![0; ngroups as usize];
// SAFETY:
// buff is the size of ngroups and getgroups reads max ngroups elements into buff
let found = unsafe { libc::getgroups(ngroups, buff.as_mut_ptr()) };
if found < 0 {
None
} else {
buff.truncate(found as usize);
buff.sort_unstable();
buff.dedup();
buff.into_iter()
.filter_map(|i| get_group_by_gid(i as gid_t))
.map(|group| group.gid)
.collect::<Vec<_>>()
.into()
}
}
/// Returns groups for a provided user name and primary group id.
///
/// # libc functions used
@ -231,9 +207,7 @@ pub mod users {
/// println!("User is a member of group #{group}");
/// }
/// ```
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn get_user_groups(username: &str, gid: gid_t) -> Option<Vec<Gid>> {
use std::ffi::CString;
// MacOS uses i32 instead of gid_t in getgrouplist for unknown reasons
#[cfg(target_os = "macos")]
let mut buff: Vec<i32> = vec![0; 1024];
@ -244,7 +218,7 @@ pub mod users {
return None;
};
let mut count = buff.len() as libc::c_int;
let mut count = buff.len() as c_int;
// MacOS uses i32 instead of gid_t in getgrouplist for unknown reasons
// SAFETY:

View File

@ -516,6 +516,7 @@ fn value_should_be_printed(
Err(_) => false,
},
Value::Binary { .. } => false,
Value::MatchPattern { .. } => false,
});
if invert {
match_found = !match_found;

View File

@ -174,7 +174,7 @@ pub fn group_by(
Value::CellPath { val, .. } => group_cell_path(val, values)?,
Value::Block { .. } | Value::Closure { .. } => {
let block: Option<Closure> = call.opt(engine_state, stack, 0)?;
group_closure(values, span, block, stack, engine_state, call)?
group_closure(&values, span, block, stack, engine_state, call)?
}
_ => {
@ -231,8 +231,9 @@ pub fn group_no_grouper(values: Vec<Value>) -> Result<IndexMap<String, Vec<Value
Ok(groups)
}
// TODO: refactor this, it's a bit of a mess
fn group_closure(
values: Vec<Value>,
values: &[Value],
span: Span,
block: Option<Closure>,
stack: &mut Stack,
@ -240,13 +241,13 @@ fn group_closure(
call: &Call,
) -> Result<IndexMap<String, Vec<Value>>, ShellError> {
let error_key = "error";
let mut groups: IndexMap<String, Vec<Value>> = IndexMap::new();
let mut keys: Vec<Result<String, ShellError>> = vec![];
let value_list = Value::list(values.to_vec(), span);
if let Some(capture_block) = &block {
let block = engine_state.get_block(capture_block.block_id);
for value in values {
for value in values {
if let Some(capture_block) = &block {
let mut stack = stack.captures_to_stack(capture_block.captures.clone());
let block = engine_state.get_block(capture_block.block_id);
let pipeline = eval_block(
engine_state,
&mut stack,
@ -256,16 +257,11 @@ fn group_closure(
call.redirect_stderr,
);
let group_key = match pipeline {
match pipeline {
Ok(s) => {
let mut s = s.into_iter();
let collection: Vec<Value> = s.into_iter().collect();
let key = match s.next() {
Some(Value::Error { .. }) | None => error_key.into(),
Some(return_value) => return_value.as_string()?,
};
if s.next().is_some() {
if collection.len() > 1 {
return Err(ShellError::GenericError {
error: "expected one value from the block".into(),
msg: "requires a table with one value for grouping".into(),
@ -275,14 +271,39 @@ fn group_closure(
});
}
key
}
Err(_) => error_key.into(),
};
let value = match collection.first() {
Some(Value::Error { .. }) | None => Value::string(error_key, span),
Some(return_value) => return_value.clone(),
};
groups.entry(group_key).or_default().push(value);
keys.push(value.as_string());
}
Err(_) => {
keys.push(Ok(error_key.into()));
}
}
}
}
let map = keys;
let block = Box::new(move |idx: usize, row: &Value| match map.get(idx) {
Some(Ok(key)) => Ok(key.clone()),
Some(Err(reason)) => Err(reason.clone()),
None => row.as_string(),
});
let grouper = &Some(block);
let mut groups: IndexMap<String, Vec<Value>> = IndexMap::new();
for (idx, value) in value_list.into_pipeline_data().into_iter().enumerate() {
let group_key = if let Some(ref grouper) = grouper {
grouper(idx, &value)
} else {
value.as_string()
};
let group = groups.entry(group_key?).or_default();
group.push(value);
}
Ok(groups)
}

View File

@ -55,7 +55,10 @@ fn from_delimited_string_to_value(
.collect::<Vec<Value>>();
rows.push(Value::record(
Record::from_raw_cols_vals(headers.clone(), output_row),
Record {
cols: headers.clone(),
vals: output_row,
},
span,
));
}

View File

@ -242,6 +242,12 @@ fn convert_to_value(
msg: "extra tokens in input file".into(),
span: expr.span,
}),
Expr::MatchPattern(..) => Err(ShellError::OutsideSpannedLabeledError {
src: original_text.to_string(),
error: "Error when loading".into(),
msg: "extra tokens in input file".into(),
span: expr.span,
}),
Expr::GlobPattern(val) => Ok(Value::string(val, span)),
Expr::ImportPattern(..) => Err(ShellError::OutsideSpannedLabeledError {
src: original_text.to_string(),
@ -403,7 +409,13 @@ fn convert_to_value(
}
for row in cells {
if cols.len() != row.len() {
let mut vals = vec![];
for cell in row {
vals.push(convert_to_value(cell, span, original_text)?);
}
if cols.len() != vals.len() {
return Err(ShellError::OutsideSpannedLabeledError {
src: original_text.to_string(),
error: "Error when loading".into(),
@ -411,13 +423,12 @@ fn convert_to_value(
span: expr.span,
});
}
let vals: Vec<Value> = row
.into_iter()
.map(|cell| convert_to_value(cell, span, original_text))
.collect::<Result<_, _>>()?;
output.push(Value::record(
Record::from_raw_cols_vals(cols.clone(), vals),
Record {
cols: cols.clone(),
vals,
},
span,
));
}

View File

@ -127,7 +127,10 @@ pub fn value_to_json_value(v: &Value) -> Result<nu_json::Value, ShellError> {
Value::List { vals, .. } => nu_json::Value::Array(json_list(vals)?),
Value::Error { error, .. } => return Err(*error.clone()),
Value::Closure { .. } | Value::Block { .. } | Value::Range { .. } => nu_json::Value::Null,
Value::Closure { .. }
| Value::Block { .. }
| Value::Range { .. }
| Value::MatchPattern { .. } => nu_json::Value::Null,
Value::Binary { val, .. } => {
nu_json::Value::Array(val.iter().map(|x| nu_json::Value::U64(*x as u64)).collect())
}

View File

@ -241,6 +241,12 @@ pub fn value_to_string(
))
}
}
Value::MatchPattern { .. } => Err(ShellError::UnsupportedInput {
msg: "match patterns are currently not nuon-compatible".to_string(),
input: "value originates from here".into(),
msg_span: span,
input_span: v.span(),
}),
Value::Nothing { .. } => Ok("null".to_string()),
Value::Range { val, .. } => Ok(format!(
"{}..{}{}",

View File

@ -148,6 +148,7 @@ fn local_into_string(value: Value, separator: &str, config: &Config) -> String {
Value::Binary { val, .. } => format!("{val:?}"),
Value::CellPath { val, .. } => val.to_string(),
Value::CustomValue { val, .. } => val.value_string(),
Value::MatchPattern { val, .. } => format!("{:?}", val),
}
}

View File

@ -94,6 +94,7 @@ fn helper(engine_state: &EngineState, v: &Value) -> Result<toml::Value, ShellErr
.collect::<Result<Vec<toml::Value>, ShellError>>()?,
),
Value::CustomValue { .. } => toml::Value::String("<Custom Value>".to_string()),
Value::MatchPattern { .. } => toml::Value::String("<Match Pattern>".to_string()),
})
}

View File

@ -34,10 +34,10 @@ impl Command for ToXml {
fn extra_usage(&self) -> &str {
r#"Every XML entry is represented via a record with tag, attribute and content fields.
To represent different types of entries different values must be written to this fields:
1. Tag entry: `{tag: <tag name> attributes: {<attr name>: "<string value>" ...} content: [<entries>]}`
2. Comment entry: `{tag: '!' attributes: null content: "<comment string>"}`
3. Processing instruction (PI): `{tag: '?<pi name>' attributes: null content: "<pi content string>"}`
4. Text: `{tag: null attributes: null content: "<text>"}`. Or as plain `<text>` instead of record.
1. Tag entry: `{tag: <tag name> attrs: {<attr name>: "<string value>" ...} content: [<entries>]}`
2. Comment entry: `{tag: '!' attrs: null content: "<comment string>"}`
3. Processing instruction (PI): `{tag: '?<pi name>' attrs: null content: "<pi content string>"}`
4. Text: `{tag: null attrs: null content: "<text>"}`. Or as plain `<text>` instead of record.
Additionally any field which is: empty record, empty list or null, can be omitted."#
}
@ -46,7 +46,7 @@ Additionally any field which is: empty record, empty list or null, can be omitte
vec![
Example {
description: "Outputs an XML string representing the contents of this table",
example: r#"{tag: note attributes: {} content : [{tag: remember attributes: {} content : [{tag: null attributes: null content : Event}]}]} | to xml"#,
example: r#"{tag: note attributes: {} content : [{tag: remember attributes: {} content : [{tag: null attrs: null content : Event}]}]} | to xml"#,
result: Some(Value::test_string(
"<note><remember>Event</remember></note>",
)),
@ -110,17 +110,6 @@ fn to_xml_entry<W: Write>(
}
if let Value::Record { val: record, .. } = &entry {
if let Some(bad_column) = find_invalid_column(record) {
return Err(ShellError::CantConvert {
to_type: "XML".into(),
from_type: "record".into(),
span: entry_span,
help: Some(format!(
"Invalid column \"{}\" in xml entry. Only \"{}\", \"{}\" and \"{}\" are permitted",
bad_column, COLUMN_TAG_NAME, COLUMN_ATTRS_NAME, COLUMN_CONTENT_NAME
)),
});
}
// If key is not found it is assumed to be nothing. This way
// user can write a tag like {tag: a content: [...]} instead
// of longer {tag: a attributes: {} content: [...]}
@ -155,12 +144,7 @@ fn to_xml_entry<W: Write>(
(Value::String { val: tag_name, .. }, attrs, children) => to_tag_like(
entry_span, tag_name, tag_span, attrs, children, top_level, writer,
),
_ => Err(ShellError::CantConvert {
to_type: "XML".into(),
from_type: "record".into(),
span: entry_span,
help: Some("Tag missing or is not a string".into()),
}),
_ => Ok(()),
}
} else {
Err(ShellError::CantConvert {
@ -172,14 +156,6 @@ fn to_xml_entry<W: Write>(
}
}
fn find_invalid_column(record: &Record) -> Option<&String> {
const VALID_COLS: [&str; 3] = [COLUMN_TAG_NAME, COLUMN_ATTRS_NAME, COLUMN_CONTENT_NAME];
record
.cols
.iter()
.find(|col| !VALID_COLS.contains(&col.as_str()))
}
/// Convert record to tag-like entry: tag, PI, comment.
fn to_tag_like<W: Write>(
entry_span: Span,

View File

@ -97,6 +97,7 @@ pub fn value_to_yaml_value(v: &Value) -> Result<serde_yaml::Value, ShellError> {
.collect::<Result<Vec<serde_yaml::Value>, ShellError>>()?,
),
Value::CustomValue { .. } => serde_yaml::Value::Null,
Value::MatchPattern { .. } => serde_yaml::Value::Null,
})
}

View File

@ -23,7 +23,6 @@ mod random;
mod removed;
mod shells;
mod sort_utils;
#[cfg(feature = "sqlite")]
mod stor;
mod strings;
mod system;

View File

@ -5,8 +5,7 @@ use base64::{alphabet, Engine};
use nu_protocol::ast::Call;
use nu_protocol::engine::{EngineState, Stack};
use nu_protocol::{
record, BufferedReader, IntoPipelineData, PipelineData, RawStream, ShellError, Span, Spanned,
Value,
record, BufferedReader, IntoPipelineData, PipelineData, RawStream, ShellError, Span, Value,
};
use ureq::{Error, ErrorKind, Request, Response};
@ -27,45 +26,29 @@ pub enum BodyType {
Unknown,
}
#[derive(Clone, Copy, PartialEq)]
pub enum RedirectMode {
Follow,
Error,
Manual,
}
// Only panics if the user agent is invalid but we define it statically so either
// it always or never fails
pub fn http_client(
allow_insecure: bool,
redirect_mode: RedirectMode,
engine_state: &EngineState,
stack: &mut Stack,
) -> Result<ureq::Agent, ShellError> {
) -> ureq::Agent {
let tls = native_tls::TlsConnector::builder()
.danger_accept_invalid_certs(allow_insecure)
.build()
.map_err(|e| ShellError::GenericError {
error: format!("Failed to build network tls: {}", e),
msg: String::new(),
span: None,
help: None,
inner: vec![],
})?;
.expect("Failed to build network tls");
let mut agent_builder = ureq::builder()
.user_agent("nushell")
.tls_connector(std::sync::Arc::new(tls));
if let RedirectMode::Manual | RedirectMode::Error = redirect_mode {
agent_builder = agent_builder.redirects(0);
}
if let Some(http_proxy) = retrieve_http_proxy_from_env(engine_state, stack) {
if let Ok(proxy) = ureq::Proxy::new(http_proxy) {
agent_builder = agent_builder.proxy(proxy);
}
};
Ok(agent_builder.build())
agent_builder.build()
}
pub fn http_parse_url(
@ -85,18 +68,6 @@ pub fn http_parse_url(
Ok((requested_url, url))
}
pub fn http_parse_redirect_mode(mode: Option<Spanned<String>>) -> Result<RedirectMode, ShellError> {
mode.map_or(Ok(RedirectMode::Follow), |v| match &v.item[..] {
"follow" | "f" => Ok(RedirectMode::Follow),
"error" | "e" => Ok(RedirectMode::Error),
"manual" | "m" => Ok(RedirectMode::Manual),
_ => Err(ShellError::TypeMismatch {
err_message: "Invalid redirect handling mode".to_string(),
span: v.span,
}),
})
}
pub fn response_to_buffer(
response: Response,
engine_state: &EngineState,
@ -481,26 +452,6 @@ fn transform_response_using_content_type(
};
}
pub fn check_response_redirection(
redirect_mode: RedirectMode,
span: Span,
response: &Result<Response, ShellErrorOrRequestError>,
) -> Result<(), ShellError> {
if let Ok(resp) = response {
if RedirectMode::Error == redirect_mode && (300..400).contains(&resp.status()) {
return Err(ShellError::NetworkFailure {
msg: format!(
"Redirect encountered when redirect handling mode was 'error' ({} {})",
resp.status(),
resp.status_text()
),
span,
});
}
}
Ok(())
}
fn request_handle_response_content(
engine_state: &EngineState,
stack: &mut Stack,

View File

@ -2,13 +2,12 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
use crate::network::http::client::{
check_response_redirection, http_client, http_parse_redirect_mode, http_parse_url,
request_add_authorization_header, request_add_custom_headers, request_handle_response,
request_set_timeout, send_request,
http_client, http_parse_url, request_add_authorization_header, request_add_custom_headers,
request_handle_response, request_set_timeout, send_request,
};
use super::client::RequestFlags;
@ -80,11 +79,6 @@ impl Command for SubCommand {
"allow-errors",
"do not fail if the server returns an error code",
Some('e'),
).named(
"redirect-mode",
SyntaxShape::String,
"What to do when encountering redirects. Default: 'follow'. Valid options: 'follow' ('f'), 'manual' ('m'), 'error' ('e').",
Some('R')
)
.filter()
.category(Category::Network)
@ -156,7 +150,6 @@ struct Arguments {
timeout: Option<Value>,
full: bool,
allow_errors: bool,
redirect: Option<Spanned<String>>,
}
fn run_delete(
@ -177,7 +170,6 @@ fn run_delete(
timeout: call.get_flag(engine_state, stack, "max-time")?,
full: call.has_flag("full"),
allow_errors: call.has_flag("allow-errors"),
redirect: call.get_flag(engine_state, stack, "redirect-mode")?,
};
helper(engine_state, stack, call, args)
@ -194,9 +186,8 @@ fn helper(
let span = args.url.span();
let ctrl_c = engine_state.ctrlc.clone();
let (requested_url, _) = http_parse_url(call, span, args.url)?;
let redirect_mode = http_parse_redirect_mode(args.redirect)?;
let client = http_client(args.insecure, redirect_mode, engine_state, stack)?;
let client = http_client(args.insecure, engine_state, stack);
let mut request = client.delete(&requested_url);
request = request_set_timeout(args.timeout, request)?;
@ -211,7 +202,6 @@ fn helper(
allow_errors: args.allow_errors,
};
check_response_redirection(redirect_mode, span, &response)?;
request_handle_response(
engine_state,
stack,

View File

@ -2,15 +2,16 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
use crate::network::http::client::{
check_response_redirection, http_client, http_parse_redirect_mode, http_parse_url,
request_add_authorization_header, request_add_custom_headers, request_handle_response,
request_set_timeout, send_request, RequestFlags,
http_client, http_parse_url, request_add_authorization_header, request_add_custom_headers,
request_handle_response, request_set_timeout, send_request,
};
use super::client::RequestFlags;
#[derive(Clone)]
pub struct SubCommand;
@ -72,12 +73,6 @@ impl Command for SubCommand {
"do not fail if the server returns an error code",
Some('e'),
)
.named(
"redirect-mode",
SyntaxShape::String,
"What to do when encountering redirects. Default: 'follow'. Valid options: 'follow' ('f'), 'manual' ('m'), 'error' ('e').",
Some('R')
)
.filter()
.category(Category::Network)
}
@ -142,7 +137,6 @@ struct Arguments {
timeout: Option<Value>,
full: bool,
allow_errors: bool,
redirect: Option<Spanned<String>>,
}
fn run_get(
@ -161,7 +155,6 @@ fn run_get(
timeout: call.get_flag(engine_state, stack, "max-time")?,
full: call.has_flag("full"),
allow_errors: call.has_flag("allow-errors"),
redirect: call.get_flag(engine_state, stack, "redirect-mode")?,
};
helper(engine_state, stack, call, args)
}
@ -177,9 +170,8 @@ fn helper(
let span = args.url.span();
let ctrl_c = engine_state.ctrlc.clone();
let (requested_url, _) = http_parse_url(call, span, args.url)?;
let redirect_mode = http_parse_redirect_mode(args.redirect)?;
let client = http_client(args.insecure, redirect_mode, engine_state, stack)?;
let client = http_client(args.insecure, engine_state, stack);
let mut request = client.get(&requested_url);
request = request_set_timeout(args.timeout, request)?;
@ -194,7 +186,6 @@ fn helper(
allow_errors: args.allow_errors,
};
check_response_redirection(redirect_mode, span, &response)?;
request_handle_response(
engine_state,
stack,

View File

@ -5,13 +5,12 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
use crate::network::http::client::{
check_response_redirection, http_client, http_parse_redirect_mode, http_parse_url,
request_add_authorization_header, request_add_custom_headers, request_handle_response_headers,
request_set_timeout, send_request,
http_client, http_parse_url, request_add_authorization_header, request_add_custom_headers,
request_handle_response_headers, request_set_timeout, send_request,
};
#[derive(Clone)]
@ -59,11 +58,6 @@ impl Command for SubCommand {
"insecure",
"allow insecure server connections when using SSL",
Some('k'),
).named(
"redirect-mode",
SyntaxShape::String,
"What to do when encountering redirects. Default: 'follow'. Valid options: 'follow' ('f'), 'manual' ('m'), 'error' ('e').",
Some('R')
)
.filter()
.category(Category::Network)
@ -120,7 +114,6 @@ struct Arguments {
user: Option<String>,
password: Option<String>,
timeout: Option<Value>,
redirect: Option<Spanned<String>>,
}
fn run_head(
@ -136,7 +129,6 @@ fn run_head(
user: call.get_flag(engine_state, stack, "user")?,
password: call.get_flag(engine_state, stack, "password")?,
timeout: call.get_flag(engine_state, stack, "max-time")?,
redirect: call.get_flag(engine_state, stack, "redirect-mode")?,
};
let ctrl_c = engine_state.ctrlc.clone();
@ -154,9 +146,8 @@ fn helper(
) -> Result<PipelineData, ShellError> {
let span = args.url.span();
let (requested_url, _) = http_parse_url(call, span, args.url)?;
let redirect_mode = http_parse_redirect_mode(args.redirect)?;
let client = http_client(args.insecure, redirect_mode, engine_state, stack)?;
let client = http_client(args.insecure, engine_state, stack);
let mut request = client.head(&requested_url);
request = request_set_timeout(args.timeout, request)?;
@ -164,7 +155,6 @@ fn helper(
request = request_add_custom_headers(args.headers, request)?;
let response = send_request(request, None, None, ctrlc);
check_response_redirection(redirect_mode, span, &response)?;
request_handle_response_headers(span, response)
}

View File

@ -10,7 +10,7 @@ use crate::network::http::client::{
request_handle_response, request_set_timeout, send_request,
};
use super::client::{RedirectMode, RequestFlags};
use super::client::RequestFlags;
#[derive(Clone)]
pub struct SubCommand;
@ -160,7 +160,7 @@ fn helper(
let ctrl_c = engine_state.ctrlc.clone();
let (requested_url, _) = http_parse_url(call, span, args.url)?;
let client = http_client(args.insecure, RedirectMode::Follow, engine_state, stack)?;
let client = http_client(args.insecure, engine_state, stack);
let mut request = client.request("OPTIONS", &requested_url);
request = request_set_timeout(args.timeout, request)?;

View File

@ -2,13 +2,12 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
use crate::network::http::client::{
check_response_redirection, http_client, http_parse_redirect_mode, http_parse_url,
request_add_authorization_header, request_add_custom_headers, request_handle_response,
request_set_timeout, send_request,
http_client, http_parse_url, request_add_authorization_header, request_add_custom_headers,
request_handle_response, request_set_timeout, send_request,
};
use super::client::RequestFlags;
@ -76,11 +75,6 @@ impl Command for SubCommand {
"allow-errors",
"do not fail if the server returns an error code",
Some('e'),
).named(
"redirect-mode",
SyntaxShape::String,
"What to do when encountering redirects. Default: 'follow'. Valid options: 'follow' ('f'), 'manual' ('m'), 'error' ('e').",
Some('R')
)
.filter()
.category(Category::Network)
@ -148,7 +142,6 @@ struct Arguments {
timeout: Option<Value>,
full: bool,
allow_errors: bool,
redirect: Option<Spanned<String>>,
}
fn run_patch(
@ -169,7 +162,6 @@ fn run_patch(
timeout: call.get_flag(engine_state, stack, "max-time")?,
full: call.has_flag("full"),
allow_errors: call.has_flag("allow-errors"),
redirect: call.get_flag(engine_state, stack, "redirect-mode")?,
};
helper(engine_state, stack, call, args)
@ -186,9 +178,8 @@ fn helper(
let span = args.url.span();
let ctrl_c = engine_state.ctrlc.clone();
let (requested_url, _) = http_parse_url(call, span, args.url)?;
let redirect_mode = http_parse_redirect_mode(args.redirect)?;
let client = http_client(args.insecure, redirect_mode, engine_state, stack)?;
let client = http_client(args.insecure, engine_state, stack);
let mut request = client.patch(&requested_url);
request = request_set_timeout(args.timeout, request)?;
@ -203,7 +194,6 @@ fn helper(
allow_errors: args.allow_errors,
};
check_response_redirection(redirect_mode, span, &response)?;
request_handle_response(
engine_state,
stack,

View File

@ -2,13 +2,12 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
use crate::network::http::client::{
check_response_redirection, http_client, http_parse_redirect_mode, http_parse_url,
request_add_authorization_header, request_add_custom_headers, request_handle_response,
request_set_timeout, send_request,
http_client, http_parse_url, request_add_authorization_header, request_add_custom_headers,
request_handle_response, request_set_timeout, send_request,
};
use super::client::RequestFlags;
@ -76,11 +75,6 @@ impl Command for SubCommand {
"allow-errors",
"do not fail if the server returns an error code",
Some('e'),
).named(
"redirect-mode",
SyntaxShape::String,
"What to do when encountering redirects. Default: 'follow'. Valid options: 'follow' ('f'), 'manual' ('m'), 'error' ('e').",
Some('R')
)
.filter()
.category(Category::Network)
@ -146,7 +140,6 @@ struct Arguments {
timeout: Option<Value>,
full: bool,
allow_errors: bool,
redirect: Option<Spanned<String>>,
}
fn run_post(
@ -167,7 +160,6 @@ fn run_post(
timeout: call.get_flag(engine_state, stack, "max-time")?,
full: call.has_flag("full"),
allow_errors: call.has_flag("allow-errors"),
redirect: call.get_flag(engine_state, stack, "redirect-mode")?,
};
helper(engine_state, stack, call, args)
@ -184,9 +176,8 @@ fn helper(
let span = args.url.span();
let ctrl_c = engine_state.ctrlc.clone();
let (requested_url, _) = http_parse_url(call, span, args.url)?;
let redirect_mode = http_parse_redirect_mode(args.redirect)?;
let client = http_client(args.insecure, redirect_mode, engine_state, stack)?;
let client = http_client(args.insecure, engine_state, stack);
let mut request = client.post(&requested_url);
request = request_set_timeout(args.timeout, request)?;
@ -201,7 +192,6 @@ fn helper(
allow_errors: args.allow_errors,
};
check_response_redirection(redirect_mode, span, &response)?;
request_handle_response(
engine_state,
stack,

View File

@ -2,13 +2,12 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};
use crate::network::http::client::{
check_response_redirection, http_client, http_parse_redirect_mode, http_parse_url,
request_add_authorization_header, request_add_custom_headers, request_handle_response,
request_set_timeout, send_request,
http_client, http_parse_url, request_add_authorization_header, request_add_custom_headers,
request_handle_response, request_set_timeout, send_request,
};
use super::client::RequestFlags;
@ -76,11 +75,6 @@ impl Command for SubCommand {
"allow-errors",
"do not fail if the server returns an error code",
Some('e'),
).named(
"redirect-mode",
SyntaxShape::String,
"What to do when encountering redirects. Default: 'follow'. Valid options: 'follow' ('f'), 'manual' ('m'), 'error' ('e').",
Some('R')
)
.filter()
.category(Category::Network)
@ -146,7 +140,6 @@ struct Arguments {
timeout: Option<Value>,
full: bool,
allow_errors: bool,
redirect: Option<Spanned<String>>,
}
fn run_put(
@ -167,7 +160,6 @@ fn run_put(
timeout: call.get_flag(engine_state, stack, "max-time")?,
full: call.has_flag("full"),
allow_errors: call.has_flag("allow-errors"),
redirect: call.get_flag(engine_state, stack, "redirect-mode")?,
};
helper(engine_state, stack, call, args)
@ -184,9 +176,8 @@ fn helper(
let span = args.url.span();
let ctrl_c = engine_state.ctrlc.clone();
let (requested_url, _) = http_parse_url(call, span, args.url)?;
let redirect_mode = http_parse_redirect_mode(args.redirect)?;
let client = http_client(args.insecure, redirect_mode, engine_state, stack)?;
let client = http_client(args.insecure, engine_state, stack);
let mut request = client.put(&requested_url);
request = request_set_timeout(args.timeout, request)?;
@ -201,7 +192,6 @@ fn helper(
allow_errors: args.allow_errors,
};
check_response_redirection(redirect_mode, span, &response)?;
request_handle_response(
engine_state,
stack,

View File

@ -7,8 +7,6 @@ mod is_terminal;
mod kill;
mod sleep;
mod term_size;
#[cfg(unix)]
mod ulimit;
mod whoami;
pub use ansi::{Ansi, AnsiLink, AnsiStrip};
@ -22,6 +20,4 @@ pub use is_terminal::IsTerminal;
pub use kill::Kill;
pub use sleep::Sleep;
pub use term_size::TermSize;
#[cfg(unix)]
pub use ulimit::ULimit;
pub use whoami::Whoami;

View File

@ -1,603 +0,0 @@
use nix::sys::resource::{rlim_t, Resource, RLIM_INFINITY};
use nu_engine::CallExt;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
SyntaxShape, Type, Value,
};
use once_cell::sync::Lazy;
/// An object contains resource related parameters
struct ResourceInfo<'a> {
name: &'a str,
desc: &'a str,
flag: char,
multiplier: rlim_t,
resource: Resource,
}
impl<'a> ResourceInfo<'a> {
/// Create a `ResourceInfo` object
fn new(
name: &'a str,
desc: &'a str,
flag: char,
multiplier: rlim_t,
resource: Resource,
) -> Self {
Self {
name,
desc,
flag,
multiplier,
resource,
}
}
/// Get unit
fn get_unit(&self) -> &str {
if self.resource == Resource::RLIMIT_CPU {
"(seconds, "
} else if self.multiplier == 1 {
"("
} else {
"(kB, "
}
}
}
impl<'a> Default for ResourceInfo<'a> {
fn default() -> Self {
Self {
name: "file-size",
desc: "Maximum size of files created by the shell",
flag: 'f',
multiplier: 1024,
resource: Resource::RLIMIT_FSIZE,
}
}
}
static RESOURCE_ARRAY: Lazy<Vec<ResourceInfo>> = Lazy::new(|| {
let resources = [
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
(
"socket-buffers",
"Maximum size of socket buffers",
'b',
1024,
Resource::RLIMIT_SBSIZE,
),
(
"core-size",
"Maximum size of core files created",
'c',
1024,
Resource::RLIMIT_CORE,
),
(
"data-size",
"Maximum size of a process's data segment",
'd',
1024,
Resource::RLIMIT_DATA,
),
#[cfg(any(target_os = "android", target_os = "linux"))]
(
"nice",
"Controls of maximum nice priority",
'e',
1,
Resource::RLIMIT_NICE,
),
(
"file-size",
"Maximum size of files created by the shell",
'f',
1024,
Resource::RLIMIT_FSIZE,
),
#[cfg(any(target_os = "android", target_os = "linux"))]
(
"pending-signals",
"Maximum number of pending signals",
'i',
1,
Resource::RLIMIT_SIGPENDING,
),
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "openbsd",
target_os = "linux",
target_os = "netbsd"
))]
(
"lock-size",
"Maximum size that may be locked into memory",
'l',
1024,
Resource::RLIMIT_MEMLOCK,
),
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "linux",
target_os = "aix",
))]
(
"resident-set-size",
"Maximum resident set size",
'm',
1024,
Resource::RLIMIT_RSS,
),
(
"file-descriptor-count",
"Maximum number of open file descriptors",
'n',
1,
Resource::RLIMIT_NOFILE,
),
#[cfg(any(target_os = "android", target_os = "linux"))]
(
"queue-size",
"Maximum bytes in POSIX message queues",
'q',
1024,
Resource::RLIMIT_MSGQUEUE,
),
#[cfg(any(target_os = "android", target_os = "linux"))]
(
"realtime-priority",
"Maximum realtime scheduling priority",
'r',
1,
Resource::RLIMIT_RTPRIO,
),
(
"stack-size",
"Maximum stack size",
's',
1024,
Resource::RLIMIT_STACK,
),
(
"cpu-time",
"Maximum amount of CPU time in seconds",
't',
1,
Resource::RLIMIT_CPU,
),
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "linux",
target_os = "aix",
))]
(
"process-count",
"Maximum number of processes available to the current user",
'u',
1,
Resource::RLIMIT_NPROC,
),
#[cfg(not(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))]
(
"virtual-memory-size",
"Maximum amount of virtual memory available to each process",
'v',
1024,
Resource::RLIMIT_AS,
),
#[cfg(target_os = "freebsd")]
(
"swap-size",
"Maximum swap space",
'w',
1024,
Resource::RLIMIT_SWAP,
),
#[cfg(any(target_os = "android", target_os = "linux"))]
(
"file-locks",
"Maximum number of file locks",
'x',
1,
Resource::RLIMIT_LOCKS,
),
#[cfg(target_os = "linux")]
(
"realtime-maxtime",
"Maximum contiguous realtime CPU time",
'y',
1,
Resource::RLIMIT_RTTIME,
),
#[cfg(target_os = "freebsd")]
(
"kernel-queues",
"Maximum number of kqueues",
'K',
1,
Resource::RLIMIT_KQUEUES,
),
#[cfg(target_os = "freebsd")]
(
"ptys",
"Maximum number of pseudo-terminals",
'P',
1,
Resource::RLIMIT_NPTS,
),
];
let mut resource_array = Vec::new();
for (name, desc, flag, multiplier, res) in resources {
resource_array.push(ResourceInfo::new(name, desc, flag, multiplier, res));
}
resource_array
});
/// Convert `rlim_t` to `Value` representation
fn limit_to_value(limit: rlim_t, multiplier: rlim_t, span: Span) -> Result<Value, ShellError> {
if limit == RLIM_INFINITY {
return Ok(Value::string("unlimited", span));
}
let val = match i64::try_from(limit / multiplier) {
Ok(v) => v,
Err(e) => {
return Err(ShellError::CantConvert {
to_type: "i64".into(),
from_type: "rlim_t".into(),
span,
help: Some(e.to_string()),
});
}
};
Ok(Value::int(val, span))
}
/// Get maximum length of all flag descriptions
fn max_desc_len(call: &Call, print_all: bool) -> usize {
let mut desc_len = 0;
let mut unit_len = 0;
for res in RESOURCE_ARRAY.iter() {
if !print_all && !call.has_flag(res.name) {
continue;
}
desc_len = res.desc.len().max(desc_len);
unit_len = res.get_unit().len().max(unit_len);
}
// Use `RLIMIT_FSIZE` limit if no resource flag provided.
if desc_len == 0 {
let res = ResourceInfo::default();
desc_len = res.desc.len().max(desc_len);
unit_len = res.get_unit().len().max(unit_len);
}
// desc.len() + unit.len() + '-X)'.len()
desc_len + unit_len + 3
}
/// Fill `ResourceInfo` to the record entry
fn fill_record(
res: &ResourceInfo,
max_len: usize,
soft: bool,
hard: bool,
span: Span,
) -> Result<Record, ShellError> {
let mut record = Record::new();
let mut desc = String::new();
desc.push_str(res.desc);
debug_assert!(res.desc.len() + res.get_unit().len() + 3 <= max_len);
let width = max_len - res.desc.len() - res.get_unit().len() - 3;
if width == 0 {
desc.push_str(format!(" {}-{})", res.get_unit(), res.flag).as_str());
} else {
desc.push_str(format!("{:>width$} {}-{})", ' ', res.get_unit(), res.flag).as_str());
}
record.push("description", Value::string(desc, span));
let (soft_limit, hard_limit) = getrlimit(res.resource)?;
if soft {
let soft_limit = limit_to_value(soft_limit, res.multiplier, span)?;
record.push("soft", soft_limit);
}
if hard {
let hard_limit = limit_to_value(hard_limit, res.multiplier, span)?;
record.push("hard", hard_limit);
}
Ok(record)
}
/// Set limits
fn set_limits(
limit_value: &Value,
res: &ResourceInfo,
soft: bool,
hard: bool,
call_span: Span,
) -> Result<(), ShellError> {
let (mut soft_limit, mut hard_limit) = getrlimit(res.resource)?;
let new_limit = parse_limit(limit_value, res, soft, soft_limit, hard_limit, call_span)?;
if hard {
hard_limit = new_limit;
}
if soft {
soft_limit = new_limit;
// Do not attempt to set the soft limit higher than the hard limit.
if (new_limit > hard_limit || new_limit == RLIM_INFINITY) && hard_limit != RLIM_INFINITY {
soft_limit = hard_limit;
}
}
setrlimit(res.resource, soft_limit, hard_limit)
}
/// Print limits
fn print_limits(
call: &Call,
print_all: bool,
soft: bool,
hard: bool,
) -> Result<PipelineData, ShellError> {
let mut output = Vec::new();
let mut print_default_limit = true;
let max_len = max_desc_len(call, print_all);
for res in RESOURCE_ARRAY.iter() {
if !print_all {
// Print specified limit.
if !call.has_flag(res.name) {
continue;
}
}
let record = fill_record(res, max_len, soft, hard, call.head)?;
output.push(Value::record(record, call.head));
if print_default_limit {
print_default_limit = false;
}
}
// Print `RLIMIT_FSIZE` limit if no resource flag provided.
if print_default_limit {
let res = ResourceInfo::default();
let record = fill_record(&res, max_len, soft, hard, call.head)?;
output.push(Value::record(record, call.head));
}
Ok(Value::list(output, call.head).into_pipeline_data())
}
/// Wrap `nix::sys::resource::getrlimit`
fn setrlimit(res: Resource, soft_limit: rlim_t, hard_limit: rlim_t) -> Result<(), ShellError> {
nix::sys::resource::setrlimit(res, soft_limit, hard_limit).map_err(|e| {
ShellError::GenericError {
error: e.to_string(),
msg: String::new(),
span: None,
help: None,
inner: vec![],
}
})
}
/// Wrap `nix::sys::resource::setrlimit`
fn getrlimit(res: Resource) -> Result<(rlim_t, rlim_t), ShellError> {
nix::sys::resource::getrlimit(res).map_err(|e| ShellError::GenericError {
error: e.to_string(),
msg: String::new(),
span: None,
help: None,
inner: vec![],
})
}
/// Parse user input
fn parse_limit(
limit_value: &Value,
res: &ResourceInfo,
soft: bool,
soft_limit: rlim_t,
hard_limit: rlim_t,
call_span: Span,
) -> Result<rlim_t, ShellError> {
match limit_value {
Value::Int { val, internal_span } => {
let value = rlim_t::try_from(*val).map_err(|e| ShellError::CantConvert {
to_type: "rlim_t".into(),
from_type: "i64".into(),
span: *internal_span,
help: Some(e.to_string()),
})?;
let (limit, overflow) = value.overflowing_mul(res.multiplier);
if overflow {
Ok(RLIM_INFINITY)
} else {
Ok(limit)
}
}
Value::Filesize { val, internal_span } => {
if res.multiplier != 1024 {
return Err(ShellError::TypeMismatch {
err_message: format!(
"filesize is not compatible with resource {:?}",
res.resource
),
span: *internal_span,
});
}
rlim_t::try_from(*val).map_err(|e| ShellError::CantConvert {
to_type: "rlim_t".into(),
from_type: "i64".into(),
span: *internal_span,
help: Some(e.to_string()),
})
}
Value::String { val, internal_span } => {
if val == "unlimited" {
Ok(RLIM_INFINITY)
} else if val == "soft" {
if soft {
Ok(hard_limit)
} else {
Ok(soft_limit)
}
} else if val == "hard" {
Ok(hard_limit)
} else {
return Err(ShellError::IncorrectValue {
msg: "Only unlimited, soft and hard are supported for strings".into(),
val_span: *internal_span,
call_span,
});
}
}
_ => Err(ShellError::TypeMismatch {
err_message: format!(
"string, int or filesize required, you provide {}",
limit_value.get_type()
),
span: limit_value.span(),
}),
}
}
#[derive(Clone)]
pub struct ULimit;
impl Command for ULimit {
fn name(&self) -> &str {
"ulimit"
}
fn usage(&self) -> &str {
"Set or get resource usage limits."
}
fn signature(&self) -> Signature {
let mut sig = Signature::build("ulimit")
.input_output_types(vec![
(Type::Nothing, Type::Table(vec![])),
(Type::Nothing, Type::Nothing),
])
.switch("soft", "Sets soft resource limit", Some('S'))
.switch("hard", "Sets hard resource limit", Some('H'))
.switch("all", "Prints all current limits", Some('a'))
.optional("limit", SyntaxShape::Any, "Limit value.")
.category(Category::Platform);
for res in RESOURCE_ARRAY.iter() {
sig = sig.switch(res.name, res.desc, Some(res.flag));
}
sig
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let mut soft = call.has_flag("soft");
let mut hard = call.has_flag("hard");
let all = call.has_flag("all");
if !hard && !soft {
// Set both hard and soft limits if neither was specified.
hard = true;
soft = true;
}
if let Some(limit_value) = call.opt::<Value>(engine_state, stack, 0)? {
let mut set_default_limit = true;
for res in RESOURCE_ARRAY.iter() {
if call.has_flag(res.name) {
set_limits(&limit_value, res, soft, hard, call.head)?;
if set_default_limit {
set_default_limit = false;
}
}
}
// Set `RLIMIT_FSIZE` limit if no resource flag provided.
if set_default_limit {
let res = ResourceInfo::default();
set_limits(&limit_value, &res, hard, soft, call.head)?;
}
Ok(PipelineData::Empty)
} else {
print_limits(call, all, soft, hard)
}
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Print all current limits",
example: "ulimit -a",
result: None,
},
Example {
description: "Print specified limits",
example: "ulimit --core-size --data-size --file-size",
result: None,
},
Example {
description: "Set limit",
example: "ulimit --core-size 102400",
result: None,
},
Example {
description: "Set stack size soft limit",
example: "ulimit -s -S 10240",
result: None,
},
Example {
description: "Set virtual memory size hard limit",
example: "ulimit -v -H 10240",
result: None,
},
Example {
description: "Set core size limit to unlimited",
example: "ulimit -c unlimited",
result: None,
},
]
}
fn search_terms(&self) -> Vec<&str> {
vec!["resource", "limits"]
}
}

View File

@ -1,19 +1,35 @@
#[cfg(feature = "sqlite")]
mod create;
#[cfg(feature = "sqlite")]
mod delete;
#[cfg(feature = "sqlite")]
mod export;
#[cfg(feature = "sqlite")]
mod import;
#[cfg(feature = "sqlite")]
mod insert;
#[cfg(feature = "sqlite")]
mod open;
#[cfg(feature = "sqlite")]
mod reset;
mod stor_;
#[cfg(feature = "sqlite")]
mod update;
#[cfg(feature = "sqlite")]
pub use create::StorCreate;
#[cfg(feature = "sqlite")]
pub use delete::StorDelete;
#[cfg(feature = "sqlite")]
pub use export::StorExport;
#[cfg(feature = "sqlite")]
pub use import::StorImport;
#[cfg(feature = "sqlite")]
pub use insert::StorInsert;
#[cfg(feature = "sqlite")]
pub use open::StorOpen;
#[cfg(feature = "sqlite")]
pub use reset::StorReset;
pub use stor_::Stor;
#[cfg(feature = "sqlite")]
pub use update::StorUpdate;

View File

@ -199,10 +199,7 @@ fn detect_columns(
};
if !(l_idx <= r_idx && (r_idx >= 0 || l_idx < (cols.len() as isize))) {
return Value::record(
Record::from_raw_cols_vals(cols, vals),
name_span,
);
return Value::record(Record { cols, vals }, name_span);
}
(l_idx.max(0) as usize, (r_idx as usize + 1).min(cols.len()))
@ -213,7 +210,7 @@ fn detect_columns(
}
}
} else {
return Value::record(Record::from_raw_cols_vals(cols, vals), name_span);
return Value::record(Record { cols, vals }, name_span);
};
// Merge Columns
@ -235,7 +232,7 @@ fn detect_columns(
vals.push(binding);
last_seg.into_iter().for_each(|v| vals.push(v));
Value::record(Record::from_raw_cols_vals(cols, vals), name_span)
Value::record(Record { cols, vals }, name_span)
})
.into_pipeline_data(ctrlc))
} else {

View File

@ -1,49 +0,0 @@
use nu_engine::get_full_help;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value,
};
#[derive(Clone)]
pub struct Format;
impl Command for Format {
fn name(&self) -> &str {
"format"
}
fn signature(&self) -> Signature {
Signature::build("format")
.category(Category::Strings)
.input_output_types(vec![(Type::Nothing, Type::String)])
}
fn usage(&self) -> &str {
"Various commands for formatting data."
}
fn extra_usage(&self) -> &str {
"You must use one of the following subcommands. Using this command as-is will only produce this help message."
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(Value::string(
get_full_help(
&Format.signature(),
&Format.examples(),
engine_state,
stack,
self.is_parser_keyword(),
),
call.head,
)
.into_pipeline_data())
}
}

View File

@ -1,9 +1,7 @@
mod date;
mod duration;
mod filesize;
mod format_;
pub use self::filesize::FormatFilesize;
pub use date::FormatDate;
pub use duration::FormatDuration;
pub use filesize::FormatFilesize;
pub use format_::Format;

View File

@ -133,22 +133,27 @@ impl Command for SubCommand {
result: Some(Value::test_string("Nu shell")),
},
Example {
description: "Trim a specific character (not the whitespace)",
example: "'=== Nu shell ===' | str trim --char '='",
result: Some(Value::test_string(" Nu shell ")),
description: "Trim a specific character",
example: "'=== Nu shell ===' | str trim --char '=' | str trim",
result: Some(Value::test_string("Nu shell")),
},
Example {
description: "Trim whitespace from the beginning of string",
example: "' Nu shell ' | str trim --left",
result: Some(Value::test_string("Nu shell ")),
},
Example {
description: "Trim a specific character",
example: "'=== Nu shell ===' | str trim --char '='",
result: Some(Value::test_string(" Nu shell ")),
},
Example {
description: "Trim whitespace from the end of string",
example: "' Nu shell ' | str trim --right",
result: Some(Value::test_string(" Nu shell")),
},
Example {
description: "Trim a specific character only from the end of the string",
description: "Trim a specific character",
example: "'=== Nu shell ===' | str trim --right --char '='",
result: Some(Value::test_string("=== Nu shell ")),
},

View File

@ -101,8 +101,6 @@ impl Command for Complete {
Ok(Value::record(record, call.head).into_pipeline_data())
}
// bubble up errors from the previous command
PipelineData::Value(Value::Error { error, .. }, _) => Err(*error),
_ => Err(ShellError::GenericError {
error: "Complete only works with external streams".into(),
msg: "complete only works on external streams".into(),

View File

@ -5,6 +5,7 @@ use itertools::Itertools;
not(target_os = "macos"),
not(target_os = "windows"),
not(target_os = "android"),
not(target_os = "ios")
))]
use nu_protocol::Span;
use nu_protocol::{
@ -18,6 +19,7 @@ use nu_protocol::{
not(target_os = "macos"),
not(target_os = "windows"),
not(target_os = "android"),
not(target_os = "ios")
))]
use procfs::WithCurrentSystemInfo;
@ -121,6 +123,7 @@ fn run_ps(engine_state: &EngineState, call: &Call) -> Result<PipelineData, Shell
not(target_os = "macos"),
not(target_os = "windows"),
not(target_os = "android"),
not(target_os = "ios")
))]
{
let proc_stat = proc

View File

@ -1,9 +1,8 @@
use nu_cmd_base::hook::eval_hook;
use nu_engine::env_to_strings;
use nu_engine::eval_expression;
use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, Expr},
ast::{Call, Expr, Expression},
did_you_mean,
engine::{Command, EngineState, Stack},
Category, Example, ListStream, PipelineData, RawStream, ShellError, Signature, Span, Spanned,
@ -114,6 +113,7 @@ pub fn create_external_command(
trim_end_newline: bool,
) -> Result<ExternalCommand, ShellError> {
let name: Spanned<String> = call.req(engine_state, stack, 0)?;
let args: Vec<Value> = call.rest(engine_state, stack, 1)?;
// Translate environment variables from Values to Strings
let env_vars_str = env_to_strings(engine_state, stack)?;
@ -132,24 +132,11 @@ pub fn create_external_command(
}
let mut spanned_args = vec![];
let args_expr: Vec<Expression> = call.positional_iter().skip(1).cloned().collect();
let mut arg_keep_raw = vec![];
for (arg, spread) in call.rest_iter(1) {
// TODO: Disallow automatic spreading entirely later. This match block will
// have to be refactored, and lists will have to be disallowed in the parser too
match eval_expression(engine_state, stack, arg)? {
for (one_arg, one_arg_expr) in args.into_iter().zip(args_expr) {
match one_arg {
Value::List { vals, .. } => {
if !spread {
nu_protocol::report_error_new(
engine_state,
&ShellError::GenericError {
error: "Automatically spreading lists is deprecated".into(),
msg: "Spreading lists automatically when calling external commands is deprecated and will be removed in 0.91.".into(),
span: Some(arg.span),
help: Some("Use the spread operator (put a '...' before the argument)".into()),
inner: vec![],
},
);
}
// turn all the strings in the array into params.
// Example: one_arg may be something like ["ls" "-a"]
// convert it to "ls" "-a"
@ -160,20 +147,15 @@ pub fn create_external_command(
}
}
val => {
if spread {
return Err(ShellError::CannotSpreadAsList { span: arg.span });
} else {
spanned_args.push(value_as_spanned(val)?);
match arg.expr {
// refer to `parse_dollar_expr` function
// the expression type of $variable_name, $"($variable_name)"
// will be Expr::StringInterpolation, Expr::FullCellPath
Expr::StringInterpolation(_) | Expr::FullCellPath(_) => {
arg_keep_raw.push(true)
}
_ => arg_keep_raw.push(false),
}
spanned_args.push(value_as_spanned(val)?);
match one_arg_expr.expr {
// refer to `parse_dollar_expr` function
// the expression type of $variable_name, $"($variable_name)"
// will be Expr::StringInterpolation, Expr::FullCellPath
Expr::StringInterpolation(_) | Expr::FullCellPath(_) => arg_keep_raw.push(true),
_ => arg_keep_raw.push(false),
}
{}
}
}
}

View File

@ -8,7 +8,7 @@ use nu_protocol::{
};
use std::time::{Duration, UNIX_EPOCH};
use sysinfo::{
Components, CpuRefreshKind, Disks, Networks, System, Users, MINIMUM_CPU_UPDATE_INTERVAL,
ComponentExt, CpuExt, CpuRefreshKind, DiskExt, NetworkExt, System, SystemExt, UserExt,
};
#[derive(Clone)]
@ -106,12 +106,14 @@ pub fn trim_cstyle_null(s: String) -> String {
}
pub fn disks(span: Span) -> Value {
let disks = Disks::new_with_refreshed_list();
let mut sys = System::new();
sys.refresh_disks();
sys.refresh_disks_list();
let mut output = vec![];
for disk in disks.list() {
for disk in sys.disks() {
let device = trim_cstyle_null(disk.name().to_string_lossy().to_string());
let typ = trim_cstyle_null(disk.file_system().to_string_lossy().to_string());
let typ = trim_cstyle_null(String::from_utf8_lossy(disk.file_system()).to_string());
let record = record! {
"device" => Value::string(device, span),
@ -129,10 +131,12 @@ pub fn disks(span: Span) -> Value {
}
pub fn net(span: Span) -> Value {
let networks = Networks::new_with_refreshed_list();
let mut sys = System::new();
sys.refresh_networks();
sys.refresh_networks_list();
let mut output = vec![];
for (iface, data) in networks.list() {
for (iface, data) in sys.networks() {
let record = record! {
"name" => Value::string(trim_cstyle_null(iface.to_string()), span),
"sent" => Value::filesize(data.total_transmitted() as i64, span),
@ -150,7 +154,7 @@ pub fn cpu(span: Span) -> Value {
// We must refresh the CPU twice a while apart to get valid usage data.
// In theory we could just sleep MINIMUM_CPU_UPDATE_INTERVAL, but I've noticed that
// that gives poor results (error of ~5%). Decided to wait 2x that long, somewhat arbitrarily
std::thread::sleep(MINIMUM_CPU_UPDATE_INTERVAL * 2);
std::thread::sleep(System::MINIMUM_CPU_UPDATE_INTERVAL * 2);
sys.refresh_cpu_specifics(CpuRefreshKind::new().with_cpu_usage());
let mut output = vec![];
@ -159,7 +163,7 @@ pub fn cpu(span: Span) -> Value {
// Round to 1DP (chosen somewhat arbitrarily) so people aren't misled by high-precision floats.
let rounded_usage = (cpu.cpu_usage() * 10.0).round() / 10.0;
let load_avg = System::load_average();
let load_avg = sys.load_average();
let load_avg = trim_cstyle_null(format!(
"{:.2}, {:.2}, {:.2}",
load_avg.one, load_avg.five, load_avg.fifteen
@ -207,39 +211,42 @@ pub fn mem(span: Span) -> Value {
}
pub fn host(span: Span) -> Value {
let mut sys = System::new();
sys.refresh_users_list();
let mut record = Record::new();
if let Some(name) = System::name() {
if let Some(name) = sys.name() {
record.push("name", Value::string(trim_cstyle_null(name), span));
}
if let Some(version) = System::os_version() {
if let Some(version) = sys.os_version() {
record.push("os_version", Value::string(trim_cstyle_null(version), span));
}
if let Some(long_version) = System::long_os_version() {
if let Some(long_version) = sys.long_os_version() {
record.push(
"long_os_version",
Value::string(trim_cstyle_null(long_version), span),
);
}
if let Some(version) = System::kernel_version() {
if let Some(version) = sys.kernel_version() {
record.push(
"kernel_version",
Value::string(trim_cstyle_null(version), span),
);
}
if let Some(hostname) = System::host_name() {
if let Some(hostname) = sys.host_name() {
record.push("hostname", Value::string(trim_cstyle_null(hostname), span));
}
record.push(
"uptime",
Value::duration(1000000000 * System::uptime() as i64, span),
Value::duration(1000000000 * sys.uptime() as i64, span),
);
// Creates a new SystemTime from the specified number of whole seconds
let d = UNIX_EPOCH + Duration::from_secs(System::boot_time());
let d = UNIX_EPOCH + Duration::from_secs(sys.boot_time());
// Create DateTime from SystemTime
let datetime = DateTime::<Local>::from(d);
// Convert to local time and then rfc3339
@ -247,16 +254,11 @@ pub fn host(span: Span) -> Value {
record.push("boot_time", Value::string(timestamp_str, span));
let users = Users::new_with_refreshed_list();
let mut users_list = vec![];
for user in users.list() {
let mut users = vec![];
for user in sys.users() {
let mut groups = vec![];
for group in user.groups() {
groups.push(Value::string(
trim_cstyle_null(group.name().to_string()),
span,
));
groups.push(Value::string(trim_cstyle_null(group.to_string()), span));
}
let record = record! {
@ -264,22 +266,24 @@ pub fn host(span: Span) -> Value {
"groups" => Value::list(groups, span),
};
users_list.push(Value::record(record, span));
users.push(Value::record(record, span));
}
if !users.is_empty() {
record.push("sessions", Value::list(users_list, span));
record.push("sessions", Value::list(users, span));
}
Value::record(record, span)
}
pub fn temp(span: Span) -> Value {
let components = Components::new_with_refreshed_list();
let mut sys = System::new();
sys.refresh_components();
sys.refresh_components_list();
let mut output = vec![];
for component in components.list() {
for component in sys.components() {
let mut record = record! {
"unit" => Value::string(component.label(), span),
"temp" => Value::float(component.temperature() as f64, span),

View File

@ -1,29 +0,0 @@
use nu_test_support::nu;
#[test]
fn basic_stdout() {
let without_complete = nu!(r#"
nu --testbin cococo test
"#);
let with_complete = nu!(r#"
(nu --testbin cococo test | complete).stdout
"#);
assert_eq!(with_complete.out, without_complete.out);
}
#[test]
fn basic_exit_code() {
let with_complete = nu!(r#"
(nu --testbin cococo test | complete).exit_code
"#);
assert_eq!(with_complete.out, "0");
}
#[test]
fn error() {
let actual = nu!("do { not-found } | complete");
assert!(actual.err.contains("executable was not found"));
}

View File

@ -197,7 +197,7 @@ fn def_wrapped_with_block() {
#[test]
fn def_wrapped_from_module() {
let actual = nu!(r#"module spam {
export def --wrapped my-echo [...rest] { nu --testbin cococo ...$rest }
export def --wrapped my-echo [...rest] { ^echo $rest }
}
use spam

View File

@ -49,7 +49,7 @@ fn exec_misc_values() {
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
nu -c 'let x = "abc"; exec nu --testbin cococo $x ...[ a b c ]'
nu -c 'let x = "abc"; exec nu --testbin cococo $x [ a b c ]'
"#
));

View File

@ -7,7 +7,6 @@ mod break_;
mod cal;
mod cd;
mod compact;
mod complete;
mod config_env_default;
mod config_nu_default;
mod continue_;
@ -107,8 +106,6 @@ mod touch;
mod transpose;
mod try_;
mod ucp;
#[cfg(unix)]
mod ulimit;
mod umkdir;
mod uniq;
mod uniq_by;

View File

@ -38,68 +38,3 @@ fn http_delete_failed_due_to_server_error() {
assert!(actual.err.contains("Bad request (400)"))
}
#[test]
fn http_delete_follows_redirect() {
let mut server = Server::new();
let _mock = server.mock("GET", "/bar").with_body("bar").create();
let _mock = server
.mock("DELETE", "/foo")
.with_status(301)
.with_header("Location", "/bar")
.create();
let actual = nu!(pipeline(
format!("http delete {url}/foo", url = server.url()).as_str()
));
assert_eq!(&actual.out, "bar");
}
#[test]
fn http_delete_redirect_mode_manual() {
let mut server = Server::new();
let _mock = server
.mock("DELETE", "/foo")
.with_status(301)
.with_body("foo")
.with_header("Location", "/bar")
.create();
let actual = nu!(pipeline(
format!(
"http delete --redirect-mode manual {url}/foo",
url = server.url()
)
.as_str()
));
assert_eq!(&actual.out, "foo");
}
#[test]
fn http_delete_redirect_mode_error() {
let mut server = Server::new();
let _mock = server
.mock("DELETE", "/foo")
.with_status(301)
.with_body("foo")
.with_header("Location", "/bar")
.create();
let actual = nu!(pipeline(
format!(
"http delete --redirect-mode error {url}/foo",
url = server.url()
)
.as_str()
));
assert!(&actual.err.contains("nu::shell::network_failure"));
assert!(&actual.err.contains(
"Redirect encountered when redirect handling mode was 'error' (301 Moved Permanently)"
));
}

View File

@ -176,71 +176,6 @@ fn http_get_full_response() {
assert_eq!(header["value"], "close");
}
#[test]
fn http_get_follows_redirect() {
let mut server = Server::new();
let _mock = server.mock("GET", "/bar").with_body("bar").create();
let _mock = server
.mock("GET", "/foo")
.with_status(301)
.with_header("Location", "/bar")
.create();
let actual = nu!(pipeline(
format!("http get {url}/foo", url = server.url()).as_str()
));
assert_eq!(&actual.out, "bar");
}
#[test]
fn http_get_redirect_mode_manual() {
let mut server = Server::new();
let _mock = server
.mock("GET", "/foo")
.with_status(301)
.with_body("foo")
.with_header("Location", "/bar")
.create();
let actual = nu!(pipeline(
format!(
"http get --redirect-mode manual {url}/foo",
url = server.url()
)
.as_str()
));
assert_eq!(&actual.out, "foo");
}
#[test]
fn http_get_redirect_mode_error() {
let mut server = Server::new();
let _mock = server
.mock("GET", "/foo")
.with_status(301)
.with_body("foo")
.with_header("Location", "/bar")
.create();
let actual = nu!(pipeline(
format!(
"http get --redirect-mode error {url}/foo",
url = server.url()
)
.as_str()
));
assert!(&actual.err.contains("nu::shell::network_failure"));
assert!(&actual.err.contains(
"Redirect encountered when redirect handling mode was 'error' (301 Moved Permanently)"
));
}
// These tests require network access; they use badssl.com which is a Google-affiliated site for testing various SSL errors.
// Revisit this if these tests prove to be flaky or unstable.

View File

@ -39,75 +39,3 @@ fn http_head_failed_due_to_server_error() {
assert!(actual.err.contains("Bad request (400)"))
}
#[test]
fn http_head_follows_redirect() {
let mut server = Server::new();
let _mock = server
.mock("HEAD", "/bar")
.with_header("bar", "bar")
.create();
let _mock = server
.mock("HEAD", "/foo")
.with_status(301)
.with_header("Location", "/bar")
.create();
let actual = nu!(pipeline(
format!(
"http head {url}/foo | (where name == bar).0.value",
url = server.url()
)
.as_str()
));
assert_eq!(&actual.out, "bar");
}
#[test]
fn http_head_redirect_mode_manual() {
let mut server = Server::new();
let _mock = server
.mock("HEAD", "/foo")
.with_status(301)
.with_body("foo")
.with_header("Location", "/bar")
.create();
let actual = nu!(pipeline(
format!(
"http head --redirect-mode manual {url}/foo | (where name == location).0.value",
url = server.url()
)
.as_str()
));
assert_eq!(&actual.out, "/bar");
}
#[test]
fn http_head_redirect_mode_error() {
let mut server = Server::new();
let _mock = server
.mock("HEAD", "/foo")
.with_status(301)
.with_body("foo")
.with_header("Location", "/bar")
.create();
let actual = nu!(pipeline(
format!(
"http head --redirect-mode error {url}/foo",
url = server.url()
)
.as_str()
));
assert!(&actual.err.contains("nu::shell::network_failure"));
assert!(&actual.err.contains(
"Redirect encountered when redirect handling mode was 'error' (301 Moved Permanently)"
));
}

View File

@ -76,68 +76,3 @@ fn http_patch_failed_due_to_unexpected_body() {
assert!(actual.err.contains("Cannot make request"))
}
#[test]
fn http_patch_follows_redirect() {
let mut server = Server::new();
let _mock = server.mock("GET", "/bar").with_body("bar").create();
let _mock = server
.mock("PATCH", "/foo")
.with_status(301)
.with_header("Location", "/bar")
.create();
let actual = nu!(pipeline(
format!("http patch {url}/foo patchbody", url = server.url()).as_str()
));
assert_eq!(&actual.out, "bar");
}
#[test]
fn http_patch_redirect_mode_manual() {
let mut server = Server::new();
let _mock = server
.mock("PATCH", "/foo")
.with_status(301)
.with_body("foo")
.with_header("Location", "/bar")
.create();
let actual = nu!(pipeline(
format!(
"http patch --redirect-mode manual {url}/foo patchbody",
url = server.url()
)
.as_str()
));
assert_eq!(&actual.out, "foo");
}
#[test]
fn http_patch_redirect_mode_error() {
let mut server = Server::new();
let _mock = server
.mock("PATCH", "/foo")
.with_status(301)
.with_body("foo")
.with_header("Location", "/bar")
.create();
let actual = nu!(pipeline(
format!(
"http patch --redirect-mode error {url}/foo patchbody",
url = server.url()
)
.as_str()
));
assert!(&actual.err.contains("nu::shell::network_failure"));
assert!(&actual.err.contains(
"Redirect encountered when redirect handling mode was 'error' (301 Moved Permanently)"
));
}

View File

@ -112,68 +112,3 @@ fn http_post_json_list_is_success() {
mock.assert();
assert!(actual.out.is_empty())
}
#[test]
fn http_post_follows_redirect() {
let mut server = Server::new();
let _mock = server.mock("GET", "/bar").with_body("bar").create();
let _mock = server
.mock("POST", "/foo")
.with_status(301)
.with_header("Location", "/bar")
.create();
let actual = nu!(pipeline(
format!("http post {url}/foo postbody", url = server.url()).as_str()
));
assert_eq!(&actual.out, "bar");
}
#[test]
fn http_post_redirect_mode_manual() {
let mut server = Server::new();
let _mock = server
.mock("POST", "/foo")
.with_status(301)
.with_body("foo")
.with_header("Location", "/bar")
.create();
let actual = nu!(pipeline(
format!(
"http post --redirect-mode manual {url}/foo postbody",
url = server.url()
)
.as_str()
));
assert_eq!(&actual.out, "foo");
}
#[test]
fn http_post_redirect_mode_error() {
let mut server = Server::new();
let _mock = server
.mock("POST", "/foo")
.with_status(301)
.with_body("foo")
.with_header("Location", "/bar")
.create();
let actual = nu!(pipeline(
format!(
"http post --redirect-mode error {url}/foo postbody",
url = server.url()
)
.as_str()
));
assert!(&actual.err.contains("nu::shell::network_failure"));
assert!(&actual.err.contains(
"Redirect encountered when redirect handling mode was 'error' (301 Moved Permanently)"
));
}

View File

@ -76,68 +76,3 @@ fn http_put_failed_due_to_unexpected_body() {
assert!(actual.err.contains("Cannot make request"))
}
#[test]
fn http_put_follows_redirect() {
let mut server = Server::new();
let _mock = server.mock("GET", "/bar").with_body("bar").create();
let _mock = server
.mock("PUT", "/foo")
.with_status(301)
.with_header("Location", "/bar")
.create();
let actual = nu!(pipeline(
format!("http put {url}/foo putbody", url = server.url()).as_str()
));
assert_eq!(&actual.out, "bar");
}
#[test]
fn http_put_redirect_mode_manual() {
let mut server = Server::new();
let _mock = server
.mock("PUT", "/foo")
.with_status(301)
.with_body("foo")
.with_header("Location", "/bar")
.create();
let actual = nu!(pipeline(
format!(
"http put --redirect-mode manual {url}/foo putbody",
url = server.url()
)
.as_str()
));
assert_eq!(&actual.out, "foo");
}
#[test]
fn http_put_redirect_mode_error() {
let mut server = Server::new();
let _mock = server
.mock("PUT", "/foo")
.with_status(301)
.with_body("foo")
.with_header("Location", "/bar")
.create();
let actual = nu!(pipeline(
format!(
"http put --redirect-mode error {url}/foo putbody",
url = server.url()
)
.as_str()
));
assert!(&actual.err.contains("nu::shell::network_failure"));
assert!(&actual.err.contains(
"Redirect encountered when redirect handling mode was 'error' (301 Moved Permanently)"
));
}

View File

@ -161,14 +161,9 @@ fn same_target_redirection_with_too_much_stderr_not_hang_nushell() {
#[test]
fn redirection_keep_exit_codes() {
Playground::setup("redirection preserves exit code", |dirs, _| {
let out = nu!(
cwd: dirs.test(),
"do -i { nu --testbin fail e> a.txt } | complete | get exit_code"
);
// needs to use contains "1", because it complete will output `Some(RawStream)`.
assert!(out.out.contains('1'));
});
let out = nu!("do -i { nu --testbin fail e> a.txt } | complete | get exit_code");
// needs to use contains "1", because it complete will output `Some(RawStream)`.
assert!(out.out.contains('1'));
}
#[test]
@ -307,29 +302,24 @@ fn separate_redirection_support_variable() {
#[test]
fn redirection_should_have_a_target() {
Playground::setup("redirection_should_have_a_target", |dirs, _| {
let scripts = [
"echo asdf o+e>",
"echo asdf o>",
"echo asdf e>",
"echo asdf o> e>",
"echo asdf o> tmp.txt e>",
"echo asdf o> e> tmp.txt",
"echo asdf o> | ignore",
"echo asdf o>; echo asdf",
];
for code in scripts {
let actual = nu!(cwd: dirs.test(), code);
assert!(
actual.err.contains("expected redirection target",),
"should be error, code: {code}",
);
assert!(
!dirs.test().join("tmp.txt").exists(),
"No file should be created on error: {code}",
);
}
});
let scripts = [
"echo asdf o+e>",
"echo asdf o>",
"echo asdf e>",
"echo asdf o> e>",
"echo asdf o> tmp.txt e>",
"echo asdf o> e> tmp.txt",
"echo asdf o> | ignore",
"echo asdf o>; echo asdf",
];
for code in scripts {
let actual = nu!(code);
assert!(
actual.err.contains("expected redirection target",),
"should be error, code: {}",
code
);
}
}
#[test]
@ -362,24 +352,8 @@ fn redirection_with_pipe() {
#[test]
fn no_duplicate_redirection() {
Playground::setup("redirection does not accept duplicate", |dirs, _| {
let actual = nu!(
cwd: dirs.test(),
"echo 3 o> a.txt o> a.txt"
);
assert!(actual.err.contains("Redirection can be set only once"));
assert!(
!dirs.test().join("a.txt").exists(),
"No file should be created on error"
);
let actual = nu!(
cwd: dirs.test(),
"echo 3 e> a.txt e> a.txt"
);
assert!(actual.err.contains("Redirection can be set only once"));
assert!(
!dirs.test().join("a.txt").exists(),
"No file should be created on error"
);
});
let actual = nu!("echo 3 o> a.txt o> a.txt");
assert!(actual.err.contains("Redirection can be set only once"));
let actual = nu!("echo 3 e> a.txt e> a.txt");
assert!(actual.err.contains("Redirection can be set only once"));
}

View File

@ -375,19 +375,6 @@ fn removes_symlink() {
});
}
#[test]
fn removes_symlink_pointing_to_directory() {
Playground::setup("rm_symlink_to_directory", |dirs, sandbox| {
sandbox.mkdir("test").symlink("test", "test_link");
nu!(cwd: sandbox.cwd(), "rm test_link");
assert!(!dirs.test().join("test_link").exists());
// The pointed directory should not be deleted.
assert!(dirs.test().join("test").exists());
});
}
#[test]
fn removes_file_after_cd() {
Playground::setup("rm_after_cd", |dirs, sandbox| {

View File

@ -139,13 +139,14 @@ fn failed_command_with_semicolon_will_not_execute_following_cmds() {
})
}
#[cfg(not(windows))]
#[test]
fn external_args_with_quoted() {
Playground::setup("external failed command with semicolon", |dirs, _| {
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
nu --testbin cococo "foo=bar 'hi'"
^echo "foo=bar 'hi'"
"#
));
@ -186,13 +187,14 @@ fn external_arg_with_variable_name() {
})
}
#[cfg(not(windows))]
#[test]
fn external_command_escape_args() {
Playground::setup("external failed command with semicolon", |dirs, _| {
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
nu --testbin cococo "\"abcd"
^echo "\"abcd"
"#
));
@ -306,12 +308,13 @@ fn can_run_batch_files_without_bat_extension() {
);
}
#[cfg(windows)]
#[test]
fn quotes_trimmed_when_shelling_out() {
// regression test for a bug where we weren't trimming quotes around string args before shelling out to cmd.exe
let actual = nu!(pipeline(
r#"
nu --testbin cococo "foo"
^echo "foo"
"#
));
@ -325,7 +328,7 @@ fn redirect_combine() {
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
run-external --redirect-combine sh ...[-c 'echo Foo; echo >&2 Bar']
run-external --redirect-combine sh [-c 'echo Foo; echo >&2 Bar']
"#
));

View File

@ -1,6 +1,6 @@
use nu_test_support::fs::{file_contents, Stub};
use nu_test_support::nu;
use nu_test_support::playground::Playground;
use nu_test_support::{nu, pipeline};
use std::io::Write;
#[test]
@ -325,45 +325,3 @@ fn save_file_correct_relative_path() {
assert_eq!(actual, "foo!");
})
}
#[test]
fn save_same_file_with_extension() {
Playground::setup("save_test_16", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(), pipeline(
"
echo 'world'
| save --raw hello.md;
open --raw hello.md
| prepend 'hello'
| save --raw --force hello.md
"
)
);
assert!(actual
.err
.contains("pipeline input and output are same file"));
})
}
#[test]
fn save_same_file_without_extension() {
Playground::setup("save_test_17", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(), pipeline(
"
echo 'world'
| save hello;
open hello
| prepend 'hello'
| save --force hello
"
)
);
assert!(actual
.err
.contains("pipeline input and output are same file"));
})
}

View File

@ -1,218 +0,0 @@
use nu_test_support::playground::Playground;
use nu_test_support::{nu, pipeline};
#[test]
fn limit_set_soft1() {
Playground::setup("limit_set_soft1", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(), pipeline(
"
let soft = (ulimit -s | first | get soft);
ulimit -s -H $soft;
let hard = (ulimit -s | first | get hard);
$soft == $hard
"
));
assert!(actual.out.contains("true"));
});
}
#[test]
fn limit_set_soft2() {
Playground::setup("limit_set_soft2", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(), pipeline(
"
let soft = (ulimit -s | first | get soft);
ulimit -s -H soft;
let hard = (ulimit -s | first | get hard);
$soft == $hard
"
));
assert!(actual.out.contains("true"));
});
}
#[test]
fn limit_set_hard1() {
Playground::setup("limit_set_hard1", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(), pipeline(
"
let hard = (ulimit -s | first | get hard);
ulimit -s $hard;
let soft = (ulimit -s | first | get soft);
$soft == $hard
"
));
assert!(actual.out.contains("true"));
});
}
#[test]
fn limit_set_hard2() {
Playground::setup("limit_set_hard2", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(), pipeline(
"
let hard = (ulimit -s | first | get hard);
ulimit -s hard;
let soft = (ulimit -s | first | get soft);
$soft == $hard
"
));
assert!(actual.out.contains("true"));
});
}
#[test]
fn limit_set_invalid1() {
Playground::setup("limit_set_invalid1", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(), pipeline(
"
let hard = (ulimit -s | first | get hard);
match $hard {
\"unlimited\" => { echo \"unlimited\" },
$x => {
let new = $x + 1;
ulimit -s $new
}
}
"
));
assert!(
actual.out.contains("unlimited")
|| actual.err.contains("EPERM: Operation not permitted")
);
});
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
#[test]
fn limit_set_invalid2() {
Playground::setup("limit_set_invalid2", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(),
"
let val = -100;
ulimit -c $val
"
);
assert!(actual.err.contains("can't convert i64 to rlim_t"));
});
}
#[test]
fn limit_set_invalid3() {
Playground::setup("limit_set_invalid3", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(),
"
ulimit -c abcd
"
);
assert!(actual
.err
.contains("Only unlimited, soft and hard are supported for strings"));
});
}
#[test]
fn limit_set_invalid4() {
Playground::setup("limit_set_invalid4", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(),
"
ulimit -c 100.0
"
);
assert!(actual.err.contains("string, int or filesize required"));
});
}
#[test]
fn limit_set_invalid5() {
use nix::sys::resource::rlim_t;
let max = (rlim_t::MAX / 1024) + 1;
Playground::setup("limit_set_invalid5", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(), pipeline(
format!(
"
let hard = (ulimit -c | first | get hard);
match $hard {{
\"unlimited\" => {{
ulimit -c -S 0;
ulimit -c {max};
ulimit -c
| first
| get soft
}},
_ => {{
echo \"unlimited\"
}}
}}
").as_str()
));
assert!(actual.out.eq("unlimited"));
});
}
#[test]
fn limit_set_filesize1() {
Playground::setup("limit_set_filesize1", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(), pipeline(
"
let hard = (ulimit -c | first | get hard);
match $hard {
\"unlimited\" => {
ulimit -c 1Mib;
ulimit -c
| first
| get soft
},
$x if $x >= 1024 * 1024 => {
ulimit -c 1Mib;
ulimit -c
| first
| get soft
}
_ => {
echo \"hard limit too small\"
}
}
"
));
assert!(actual.out.eq("1024") || actual.out.eq("hard limit too small"));
});
}
#[test]
fn limit_set_filesize2() {
Playground::setup("limit_set_filesize2", |dirs, _sandbox| {
let actual = nu!(
cwd: dirs.test(),
"
ulimit -n 10Kib
"
);
assert!(actual
.err
.contains("filesize is not compatible with resource RLIMIT_NOFILE"));
});
}

View File

@ -22,39 +22,3 @@ fn table_to_xml_text_and_from_xml_text_back_into_table() {
assert_eq!(actual.out, "true");
}
#[test]
fn to_xml_error_unknown_column() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
{tag: a bad_column: b} | to xml
"#
));
assert!(actual.err.contains("Invalid column \"bad_column\""));
}
#[test]
fn to_xml_error_no_tag() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
{attributes: {a: b c: d}} | to xml
"#
));
assert!(actual.err.contains("Tag missing"));
}
#[test]
fn to_xml_error_tag_not_string() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
{tag: 1 attributes: {a: b c: d}} | to xml
"#
));
assert!(actual.err.contains("not a string"));
}

View File

@ -1,8 +1,8 @@
use nu_protocol::{
ast::{Call, Expression},
ast::Call,
engine::{EngineState, Stack, StateWorkingSet},
eval_const::eval_constant,
FromValue, ShellError, Value,
FromValue, ShellError,
};
use crate::eval_expression;
@ -34,10 +34,6 @@ pub trait CallExt {
starting_pos: usize,
) -> Result<Vec<T>, ShellError>;
fn rest_iter_flattened<F>(&self, start: usize, eval: F) -> Result<Vec<Value>, ShellError>
where
F: FnMut(&Expression) -> Result<Value, ShellError>;
fn opt<T: FromValue>(
&self,
engine_state: &EngineState,
@ -74,7 +70,7 @@ impl CallExt for Call {
name: &str,
) -> Result<Option<T>, ShellError> {
if let Some(expr) = self.get_flag_expr(name) {
let result = eval_expression(engine_state, stack, expr)?;
let result = eval_expression(engine_state, stack, &expr)?;
FromValue::from_value(result).map(Some)
} else {
Ok(None)
@ -87,7 +83,7 @@ impl CallExt for Call {
name: &str,
) -> Result<Option<T>, ShellError> {
if let Some(expr) = self.get_flag_expr(name) {
let result = eval_constant(working_set, expr)?;
let result = eval_constant(working_set, &expr)?;
FromValue::from_value(result).map(Some)
} else {
Ok(None)
@ -102,9 +98,8 @@ impl CallExt for Call {
) -> Result<Vec<T>, ShellError> {
let mut output = vec![];
for result in self.rest_iter_flattened(starting_pos, |expr| {
eval_expression(engine_state, stack, expr)
})? {
for expr in self.positional_iter().skip(starting_pos) {
let result = eval_expression(engine_state, stack, expr)?;
output.push(FromValue::from_value(result)?);
}
@ -118,36 +113,14 @@ impl CallExt for Call {
) -> Result<Vec<T>, ShellError> {
let mut output = vec![];
for result in
self.rest_iter_flattened(starting_pos, |expr| eval_constant(working_set, expr))?
{
for expr in self.positional_iter().skip(starting_pos) {
let result = eval_constant(working_set, expr)?;
output.push(FromValue::from_value(result)?);
}
Ok(output)
}
fn rest_iter_flattened<F>(&self, start: usize, mut eval: F) -> Result<Vec<Value>, ShellError>
where
F: FnMut(&Expression) -> Result<Value, ShellError>,
{
let mut output = Vec::new();
for (expr, spread) in self.rest_iter(start) {
let result = eval(expr)?;
if spread {
match result {
Value::List { mut vals, .. } => output.append(&mut vals),
_ => return Err(ShellError::CannotSpreadAsList { span: expr.span }),
}
} else {
output.push(result);
}
}
Ok(output)
}
fn opt<T: FromValue>(
&self,
engine_state: &EngineState,

View File

@ -1,9 +1,9 @@
use crate::{call_ext::CallExt, current_dir_str, get_full_help};
use crate::{current_dir_str, get_full_help};
use nu_path::expand_path_with;
use nu_protocol::{
ast::{
Argument, Assignment, Block, Call, Expr, Expression, ExternalArgument, PathMember,
PipelineElement, Redirection,
Argument, Assignment, Block, Call, Expr, Expression, PathMember, PipelineElement,
Redirection,
},
engine::{Closure, EngineState, Stack},
eval_base::Eval,
@ -66,11 +66,11 @@ pub fn eval_call(
if let Some(rest_positional) = decl.signature().rest_positional {
let mut rest_items = vec![];
for result in call.rest_iter_flattened(
for arg in call.positional_iter().skip(
decl.signature().required_positional.len()
+ decl.signature().optional_positional.len(),
|expr| eval_expression(engine_state, caller_stack, expr),
)? {
) {
let result = eval_expression(engine_state, caller_stack, arg)?;
rest_items.push(result);
}
@ -182,7 +182,7 @@ fn eval_external(
engine_state: &EngineState,
stack: &mut Stack,
head: &Expression,
args: &[ExternalArgument],
args: &[Expression],
input: PipelineData,
redirect_target: RedirectTarget,
is_subexpression: bool,
@ -198,10 +198,7 @@ fn eval_external(
call.add_positional(head.clone());
for arg in args {
match arg {
ExternalArgument::Regular(expr) => call.add_positional(expr.clone()),
ExternalArgument::Spread(expr) => call.add_spread(expr.clone()),
}
call.add_positional(arg.clone())
}
match redirect_target {
@ -950,7 +947,7 @@ impl Eval for EvalRuntime {
engine_state: &EngineState,
stack: &mut Stack,
head: &Expression,
args: &[ExternalArgument],
args: &[Expression],
is_subexpression: bool,
_: Span,
) -> Result<Value, ShellError> {
@ -1102,18 +1099,7 @@ impl Eval for EvalRuntime {
.get_block(block_id)
.captures
.iter()
.map(|&id| {
stack
.get_var(id, span)
.or_else(|_| {
engine_state
.get_var(id)
.const_val
.clone()
.ok_or(ShellError::VariableNotFoundAtRuntime { span })
})
.map(|var| (id, var))
})
.map(|&id| stack.get_var(id, span).map(|var| (id, var)))
.collect::<Result<_, _>>()?;
Ok(Value::closure(Closure { block_id, captures }, span))

View File

@ -20,10 +20,10 @@ nu-table = { path = "../nu-table", version = "0.88.2" }
nu-json = { path = "../nu-json", version = "0.88.2" }
nu-utils = { path = "../nu-utils", version = "0.88.2" }
terminal_size = "0.3"
terminal_size = "0.2"
strip-ansi-escapes = "0.2.0"
crossterm = "0.27"
ratatui = "0.23"
ansi-str = "0.8"
unicode-width = "0.1"
lscolors = { version = "0.16", default-features = false, features = ["nu-ansi-term"] }
lscolors = { version = "0.15", default-features = false, features = ["nu-ansi-term"] }

View File

@ -15,7 +15,7 @@ nu-protocol = { path = "../nu-protocol", version = "0.88.2" }
reedline = { version = "0.27" }
crossbeam-channel = "0.5.8"
lsp-types = "0.95.0"
lsp-types = "0.94.1"
lsp-server = "0.7.5"
miette = "5.10"
ropey = "1.6.1"

View File

@ -312,7 +312,7 @@ impl LanguageServer {
}
}
}
Id::Value(_) => {}
_ => {}
}
None
}
@ -347,22 +347,11 @@ impl LanguageServer {
Id::Declaration(decl_id) => {
let decl = working_set.get_decl(decl_id);
let mut description = String::new();
// First description
description.push_str(&format!("{}\n", decl.usage().replace('\r', "")));
// Additional description
if !decl.extra_usage().is_empty() {
description.push_str(&format!("\n{}\n", decl.extra_usage()));
}
// Usage
description.push_str("### Usage \n```\n");
let mut description = "\n### Signature\n```\n".to_string();
let signature = decl.signature();
description.push_str(&format!(" {}", signature.name));
if !signature.named.is_empty() {
description.push_str(" {flags}");
description.push_str(" {flags}")
}
for required_arg in &signature.required_positional {
description.push_str(&format!(" <{}>", required_arg.name));
@ -374,39 +363,6 @@ impl LanguageServer {
description.push_str(&format!(" <...{}>", arg.name));
}
description.push_str("\n```\n");
// Flags
if !signature.named.is_empty() {
description.push_str("\n### Flags\n\n");
let mut first = true;
for named in &signature.named {
if first {
first = false;
} else {
description.push('\n');
}
description.push_str(" ");
if let Some(short_flag) = &named.short {
description.push_str(&format!("`-{short_flag}`"));
}
if !named.long.is_empty() {
if named.short.is_some() {
description.push_str(", ");
}
description.push_str(&format!("`--{}`", named.long));
}
if let Some(arg) = &named.arg {
description.push_str(&format!(" `<{}>`", arg.to_type()));
}
if !named.desc.is_empty() {
description.push_str(&format!(" - {}", named.desc));
}
description.push('\n');
}
description.push('\n');
}
// Parameters
if !signature.required_positional.is_empty()
|| !signature.optional_positional.is_empty()
|| signature.rest_positional.is_some()
@ -414,10 +370,10 @@ impl LanguageServer {
description.push_str("\n### Parameters\n\n");
let mut first = true;
for required_arg in &signature.required_positional {
if first {
first = false;
} else {
if !first {
description.push('\n');
} else {
first = false;
}
description.push_str(&format!(
" `{}: {}`",
@ -430,10 +386,10 @@ impl LanguageServer {
description.push('\n');
}
for optional_arg in &signature.optional_positional {
if first {
first = false;
} else {
if !first {
description.push('\n');
} else {
first = false;
}
description.push_str(&format!(
" `{}: {}`",
@ -461,10 +417,36 @@ impl LanguageServer {
}
description.push('\n');
}
// Input/output types
if !signature.named.is_empty() {
description.push_str("\n### Flags\n\n");
let mut first = true;
for named in &signature.named {
if !first {
description.push('\n');
} else {
first = false;
}
description.push_str(" ");
if let Some(short_flag) = &named.short {
description.push_str(&format!("`-{}`", short_flag));
}
if !named.long.is_empty() {
if named.short.is_some() {
description.push_str(", ")
}
description.push_str(&format!("`--{}`", named.long));
}
if let Some(arg) = &named.arg {
description.push_str(&format!(" `<{}>`", arg.to_type()))
}
if !named.desc.is_empty() {
description.push_str(&format!(" - {}", named.desc));
}
}
description.push('\n');
}
if !signature.input_output_types.is_empty() {
description.push_str("\n### Input/output types\n");
description.push_str("\n### Input/output\n");
description.push_str("\n```\n");
for input_output in &signature.input_output_types {
description
@ -472,8 +454,14 @@ impl LanguageServer {
}
description.push_str("\n```\n");
}
// Examples
description.push_str(&format!(
"### Usage\n {}\n",
decl.usage().replace('\r', "")
));
if !decl.extra_usage().is_empty() {
description
.push_str(&format!("\n### Extra usage:\n {}\n", decl.extra_usage()));
}
if !decl.examples().is_empty() {
description.push_str("### Example(s)\n");
for example in decl.examples() {
@ -590,9 +578,8 @@ mod tests {
},
request::{Completion, GotoDefinition, HoverRequest, Initialize, Request, Shutdown},
CompletionParams, DidChangeTextDocumentParams, DidOpenTextDocumentParams,
GotoDefinitionParams, InitializeParams, InitializedParams, PartialResultParams,
TextDocumentContentChangeEvent, TextDocumentIdentifier, TextDocumentItem,
TextDocumentPositionParams, Url, WorkDoneProgressParams,
GotoDefinitionParams, InitializeParams, InitializedParams, TextDocumentContentChangeEvent,
TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, Url,
};
use nu_test_support::fs::{fixtures, root};
use std::sync::mpsc::Receiver;
@ -684,8 +671,8 @@ mod tests {
character: 0,
},
},
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
work_done_progress_params: Default::default(),
partial_result_params: Default::default(),
})
.unwrap(),
}))
@ -790,8 +777,8 @@ mod tests {
text_document: TextDocumentIdentifier { uri },
position: lsp_types::Position { line, character },
},
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
work_done_progress_params: Default::default(),
partial_result_params: Default::default(),
})
.unwrap(),
}))
@ -907,7 +894,7 @@ mod tests {
text_document: TextDocumentIdentifier { uri },
position: lsp_types::Position { line, character },
},
work_done_progress_params: WorkDoneProgressParams::default(),
work_done_progress_params: Default::default(),
})
.unwrap(),
}))
@ -970,7 +957,7 @@ mod tests {
serde_json::json!({
"contents": {
"kind": "markdown",
"value": "Renders some greeting message\n### Usage \n```\n hello {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n"
"value": "\n### Signature\n```\n hello {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n### Usage\n Renders some greeting message\n"
}
})
);
@ -987,8 +974,8 @@ mod tests {
text_document: TextDocumentIdentifier { uri },
position: lsp_types::Position { line, character },
},
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
work_done_progress_params: Default::default(),
partial_result_params: Default::default(),
context: None,
})
.unwrap(),

View File

@ -121,7 +121,7 @@ mod tests {
serde_json::json!({
"contents": {
"kind": "markdown",
"value": "Create a variable and give it a value.\n\nThis command is a parser keyword. For details, check:\n https://www.nushell.sh/book/thinking_in_nu.html\n### Usage \n```\n let {flags} <var_name> <initial_value>\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n\n### Parameters\n\n `var_name: any` - Variable name.\n\n `initial_value: any` - Equals sign followed by value.\n\n\n### Input/output types\n\n```\n any | nothing\n\n```\n### Example(s)\n Set a variable to a value\n```\n let x = 10\n```\n Set a variable to the result of an expression\n```\n let x = 10 + 100\n```\n Set a variable based on the condition\n```\n let x = if false { -1 } else { 1 }\n```\n"
"value": "\n### Signature\n```\n let {flags} <var_name> <initial_value>\n```\n\n### Parameters\n\n `var_name: any` - Variable name.\n\n `initial_value: any` - Equals sign followed by value.\n\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n### Input/output\n\n```\n any | nothing\n\n```\n### Usage\n Create a variable and give it a value.\n\n### Extra usage:\n This command is a parser keyword. For details, check:\n https://www.nushell.sh/book/thinking_in_nu.html\n### Example(s)\n Set a variable to a value\n```\n let x = 10\n```\n Set a variable to the result of an expression\n```\n let x = 10 + 100\n```\n Set a variable based on the condition\n```\n let x = if false { -1 } else { 1 }\n```\n"
}
})
);
@ -162,7 +162,7 @@ hello"#,
serde_json::json!({
"contents": {
"kind": "markdown",
"value": "Renders some updated greeting message\n### Usage \n```\n hello {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n"
"value": "\n### Signature\n```\n hello {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n### Usage\n Renders some updated greeting message\n"
}
})
);
@ -207,7 +207,7 @@ hello"#,
serde_json::json!({
"contents": {
"kind": "markdown",
"value": "Renders some updated greeting message\n### Usage \n```\n hello {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n"
"value": "\n### Signature\n```\n hello {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n### Usage\n Renders some updated greeting message\n"
}
})
);

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