forked from extern/nushell
Compare commits
30 Commits
dependabot
...
main
Author | SHA1 | Date | |
---|---|---|---|
c74cff213e | |||
2ea5819b02 | |||
1115190a49 | |||
7132c5ad02 | |||
1920ece759 | |||
6f59abaf43 | |||
5f7425a7b4 | |||
1ab9ec3ebc | |||
f2095ed0cc | |||
7e26b4fcc2 | |||
ad95e4cc27 | |||
ee5a18167c | |||
75c9e3e5df | |||
77f10eb270 | |||
42bb42a2e1 | |||
f597380112 | |||
b92b4120dc | |||
de5ad5de19 | |||
64695cd67c | |||
21b3eeed99 | |||
a86a7e6c29 | |||
15421dc45e | |||
9522052063 | |||
ba880277bf | |||
40241e9be6 | |||
34f3da7150 | |||
534287ed65 | |||
913c2b8d1c | |||
aeffa188f0 | |||
543a25599c |
1
.github/.typos.toml
vendored
1
.github/.typos.toml
vendored
@ -12,3 +12,4 @@ IIF = "IIF"
|
|||||||
numer = "numer"
|
numer = "numer"
|
||||||
ratatui = "ratatui"
|
ratatui = "ratatui"
|
||||||
doas = "doas"
|
doas = "doas"
|
||||||
|
wheres = "wheres"
|
||||||
|
2
.github/workflows/nightly-build.yml
vendored
2
.github/workflows/nightly-build.yml
vendored
@ -119,7 +119,7 @@ jobs:
|
|||||||
os: ubuntu-20.04
|
os: ubuntu-20.04
|
||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
- target: riscv64gc-unknown-linux-gnu
|
- target: riscv64gc-unknown-linux-gnu
|
||||||
os: ubuntu-20.04
|
os: ubuntu-latest
|
||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
|
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
|
8
.github/workflows/release-pkg.nu
vendored
8
.github/workflows/release-pkg.nu
vendored
@ -82,8 +82,8 @@ print $'Start building ($bin)...'; hr-line
|
|||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
# Build for Ubuntu and macOS
|
# Build for Ubuntu and macOS
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
if $os in [$USE_UBUNTU, 'macos-latest'] {
|
if $os in [$USE_UBUNTU, 'macos-latest', 'ubuntu-latest'] {
|
||||||
if $os == $USE_UBUNTU {
|
if $os starts-with ubuntu {
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt-get install libxcb-composite0-dev -y
|
sudo apt-get install libxcb-composite0-dev -y
|
||||||
}
|
}
|
||||||
@ -106,7 +106,7 @@ if $os in [$USE_UBUNTU, 'macos-latest'] {
|
|||||||
_ => {
|
_ => {
|
||||||
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
|
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
|
||||||
# Actually just for x86_64-unknown-linux-musl target
|
# Actually just for x86_64-unknown-linux-musl target
|
||||||
if $os == $USE_UBUNTU { sudo apt install musl-tools -y }
|
if $os starts-with ubuntu { sudo apt install musl-tools -y }
|
||||||
cargo-build-nu $flags
|
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
|
# Create a release archive and send it to output for the following steps
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
cd $dist; print $'(char nl)Creating release archive...'; hr-line
|
cd $dist; print $'(char nl)Creating release archive...'; hr-line
|
||||||
if $os in [$USE_UBUNTU, 'macos-latest'] {
|
if $os in [$USE_UBUNTU, 'macos-latest', 'ubuntu-latest'] {
|
||||||
|
|
||||||
let files = (ls | get name)
|
let files = (ls | get name)
|
||||||
let dest = if $env.RELEASE_TYPE == 'full' { $'($bin)-($version)-($FULL_NAME)' } else { $'($bin)-($version)-($target)' }
|
let dest = if $env.RELEASE_TYPE == 'full' { $'($bin)-($version)-($FULL_NAME)' } else { $'($bin)-($version)-($target)' }
|
||||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -66,7 +66,7 @@ jobs:
|
|||||||
os: ubuntu-20.04
|
os: ubuntu-20.04
|
||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
- target: riscv64gc-unknown-linux-gnu
|
- target: riscv64gc-unknown-linux-gnu
|
||||||
os: ubuntu-20.04
|
os: ubuntu-latest
|
||||||
target_rustflags: ''
|
target_rustflags: ''
|
||||||
|
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
|
2
.github/workflows/typos.yml
vendored
2
.github/workflows/typos.yml
vendored
@ -10,6 +10,6 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Check spelling
|
- name: Check spelling
|
||||||
uses: crate-ci/typos@v1.16.25
|
uses: crate-ci/typos@v1.17.0
|
||||||
with:
|
with:
|
||||||
config: ./.github/.typos.toml
|
config: ./.github/.typos.toml
|
||||||
|
105
Cargo.lock
generated
105
Cargo.lock
generated
@ -1281,7 +1281,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5"
|
checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"rustix 0.38.28",
|
"rustix",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1802,9 +1802,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ical"
|
name = "ical"
|
||||||
version = "0.8.0"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "356d82bd58997815d55ea6f9081bd4cac149e50ca943f7a4f7c050fec7271c1f"
|
checksum = "26393c372d4c4d51616084afe36c0b44e4467febaa6f91f11f789094b4863bf9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
@ -1884,17 +1884,6 @@ version = "0.3.13"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0508c56cfe9bfd5dfeb0c22ab9a6abfda2f27bdca422132e494266351ed8d83c"
|
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]]
|
[[package]]
|
||||||
name = "is-docker"
|
name = "is-docker"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -1911,7 +1900,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
|
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"hermit-abi",
|
||||||
"rustix 0.38.28",
|
"rustix",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2233,12 +2222,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "linux-raw-sys"
|
|
||||||
version = "0.3.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.4.12"
|
version = "0.4.12"
|
||||||
@ -2293,9 +2276,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lsp-types"
|
name = "lsp-types"
|
||||||
version = "0.94.1"
|
version = "0.95.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c66bfd44a06ae10647fe3f8214762e9369fd4248df1350924b4ef9e770a85ea1"
|
checksum = "158c1911354ef73e8fe42da6b10c0484cb65c7f1007f28022e847706c1ab6984"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"serde",
|
"serde",
|
||||||
@ -2697,7 +2680,7 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"reedline",
|
"reedline",
|
||||||
"rstest",
|
"rstest",
|
||||||
"sysinfo",
|
"sysinfo 0.30.4",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"uuid",
|
"uuid",
|
||||||
"which 5.0.0",
|
"which 5.0.0",
|
||||||
@ -2876,7 +2859,7 @@ dependencies = [
|
|||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"sha2",
|
"sha2",
|
||||||
"sysinfo",
|
"sysinfo 0.30.4",
|
||||||
"tabled",
|
"tabled",
|
||||||
"terminal_size 0.3.0",
|
"terminal_size 0.3.0",
|
||||||
"titlecase",
|
"titlecase",
|
||||||
@ -2924,7 +2907,7 @@ dependencies = [
|
|||||||
"nu-utils",
|
"nu-utils",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
"strip-ansi-escapes",
|
"strip-ansi-escapes",
|
||||||
"terminal_size 0.2.6",
|
"terminal_size 0.3.0",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3060,8 +3043,8 @@ dependencies = [
|
|||||||
"ntapi",
|
"ntapi",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"procfs",
|
"procfs",
|
||||||
"sysinfo",
|
"sysinfo 0.30.4",
|
||||||
"winapi",
|
"windows 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4089,7 +4072,7 @@ dependencies = [
|
|||||||
"polars-error",
|
"polars-error",
|
||||||
"rayon",
|
"rayon",
|
||||||
"smartstring",
|
"smartstring",
|
||||||
"sysinfo",
|
"sysinfo 0.29.11",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4194,7 +4177,7 @@ dependencies = [
|
|||||||
"hex",
|
"hex",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"procfs-core",
|
"procfs-core",
|
||||||
"rustix 0.38.28",
|
"rustix",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4392,12 +4375,12 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "reedline"
|
name = "reedline"
|
||||||
version = "0.27.1"
|
version = "0.27.1"
|
||||||
source = "git+https://github.com/nushell/reedline.git?branch=main#e097b88dab538705c7b165cf3a1f5cf3a74a23bb"
|
source = "git+https://github.com/nushell/reedline.git?branch=main#b68ce33c750b700bb4f8adccbc2e160d1b7c8742"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"fd-lock",
|
"fd-lock",
|
||||||
"itertools 0.10.5",
|
"itertools 0.12.0",
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
"serde",
|
"serde",
|
||||||
@ -4644,20 +4627,6 @@ dependencies = [
|
|||||||
"semver",
|
"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]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.28"
|
version = "0.38.28"
|
||||||
@ -4667,7 +4636,7 @@ dependencies = [
|
|||||||
"bitflags 2.4.1",
|
"bitflags 2.4.1",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys 0.4.12",
|
"linux-raw-sys",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4899,9 +4868,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shadow-rs"
|
name = "shadow-rs"
|
||||||
version = "0.25.0"
|
version = "0.26.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "615d846f7174a0850dca101bca72f6913e3376a64c5fda2b965d7fc3d1ff60cb"
|
checksum = "878cb1e3162d98ee1016b832efbb683ad6302b462a2894c54f488dc0bd96f11c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"const_format",
|
"const_format",
|
||||||
"is_debug",
|
"is_debug",
|
||||||
@ -5280,10 +5249,24 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"ntapi",
|
"ntapi",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rayon",
|
|
||||||
"winapi",
|
"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",
|
||||||
|
"libc",
|
||||||
|
"ntapi",
|
||||||
|
"once_cell",
|
||||||
|
"rayon",
|
||||||
|
"windows 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tabled"
|
name = "tabled"
|
||||||
version = "0.14.0"
|
version = "0.14.0"
|
||||||
@ -5311,7 +5294,7 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"rustix 0.38.28",
|
"rustix",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -5345,23 +5328,13 @@ dependencies = [
|
|||||||
"winapi",
|
"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]]
|
[[package]]
|
||||||
name = "terminal_size"
|
name = "terminal_size"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
|
checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustix 0.38.28",
|
"rustix",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -6069,7 +6042,7 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
"home",
|
"home",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix 0.38.28",
|
"rustix",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -6081,7 +6054,7 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
"home",
|
"home",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix 0.38.28",
|
"rustix",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -6396,8 +6369,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "a7dae5072fe1f8db8f8d29059189ac175196e410e40ba42d5d4684ae2f750995"
|
checksum = "a7dae5072fe1f8db8f8d29059189ac175196e410e40ba42d5d4684ae2f750995"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys 0.4.12",
|
"linux-raw-sys",
|
||||||
"rustix 0.38.28",
|
"rustix",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -37,7 +37,7 @@ miette = { version = "5.10", features = ["fancy-no-backtrace"] }
|
|||||||
once_cell = "1.18"
|
once_cell = "1.18"
|
||||||
percent-encoding = "2"
|
percent-encoding = "2"
|
||||||
pathdiff = "0.2"
|
pathdiff = "0.2"
|
||||||
sysinfo = "0.29"
|
sysinfo = "0.30"
|
||||||
unicode-segmentation = "1.10"
|
unicode-segmentation = "1.10"
|
||||||
uuid = { version = "1.6.0", features = ["v4"] }
|
uuid = { version = "1.6.0", features = ["v4"] }
|
||||||
which = "5.0.0"
|
which = "5.0.0"
|
||||||
|
@ -354,9 +354,7 @@ impl NuCompleter {
|
|||||||
if let Some(external_result) = self.external_completion(
|
if let Some(external_result) = self.external_completion(
|
||||||
block_id, &spans, offset, new_span,
|
block_id, &spans, offset, new_span,
|
||||||
) {
|
) {
|
||||||
if !external_result.is_empty() {
|
return external_result;
|
||||||
return external_result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ use std::{
|
|||||||
sync::atomic::Ordering,
|
sync::atomic::Ordering,
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
use sysinfo::SystemExt;
|
use sysinfo::System;
|
||||||
|
|
||||||
// According to Daniel Imms @Tyriar, we need to do these this way:
|
// According to Daniel Imms @Tyriar, we need to do these this way:
|
||||||
// <133 A><prompt><133 B><command><133 C><command output>
|
// <133 A><prompt><133 B><command><133 C><command output>
|
||||||
@ -135,17 +135,6 @@ pub fn evaluate_repl(
|
|||||||
use_color,
|
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 {
|
if let Some(s) = prerun_command {
|
||||||
eval_source(
|
eval_source(
|
||||||
engine_state,
|
engine_state,
|
||||||
@ -428,7 +417,7 @@ pub fn evaluate_repl(
|
|||||||
|
|
||||||
match input {
|
match input {
|
||||||
Ok(Signal::Success(s)) => {
|
Ok(Signal::Success(s)) => {
|
||||||
let hostname = sys.host_name();
|
let hostname = System::host_name();
|
||||||
let history_supports_meta =
|
let history_supports_meta =
|
||||||
matches!(config.history_file_format, HistoryFileFormat::Sqlite);
|
matches!(config.history_file_format, HistoryFileFormat::Sqlite);
|
||||||
if history_supports_meta && !s.is_empty() && line_editor.has_last_command_context()
|
if history_supports_meta && !s.is_empty() && line_editor.has_last_command_context()
|
||||||
|
@ -385,6 +385,7 @@ fn find_matching_block_end_in_expr(
|
|||||||
Argument::Named((_, _, opt_expr)) => opt_expr.as_ref(),
|
Argument::Named((_, _, opt_expr)) => opt_expr.as_ref(),
|
||||||
Argument::Positional(inner_expr) => Some(inner_expr),
|
Argument::Positional(inner_expr) => Some(inner_expr),
|
||||||
Argument::Unknown(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 {
|
if let Some(inner_expr) = opt_expr {
|
||||||
|
@ -20,10 +20,10 @@ nu-ansi-term = "0.49.0"
|
|||||||
|
|
||||||
fancy-regex = "0.12"
|
fancy-regex = "0.12"
|
||||||
itertools = "0.12"
|
itertools = "0.12"
|
||||||
shadow-rs = { version = "0.25", default-features = false }
|
shadow-rs = { version = "0.26", default-features = false }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
shadow-rs = { version = "0.25", default-features = false }
|
shadow-rs = { version = "0.26", default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
mimalloc = []
|
mimalloc = []
|
||||||
|
@ -80,7 +80,7 @@ serde_json = "1.0"
|
|||||||
serde_urlencoded = "0.7"
|
serde_urlencoded = "0.7"
|
||||||
serde_yaml = "0.9"
|
serde_yaml = "0.9"
|
||||||
sha2 = "0.10"
|
sha2 = "0.10"
|
||||||
sysinfo = "0.29"
|
sysinfo = "0.30"
|
||||||
tabled = { version = "0.14.0", features = ["color"], default-features = false }
|
tabled = { version = "0.14.0", features = ["color"], default-features = false }
|
||||||
terminal_size = "0.3"
|
terminal_size = "0.3"
|
||||||
titlecase = "2.0"
|
titlecase = "2.0"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use nu_engine::eval_expression;
|
use nu_engine::{eval_expression, CallExt};
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
@ -48,8 +48,7 @@ impl Command for BytesBuild {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
for expr in call.positional_iter() {
|
for val in call.rest_iter_flattened(0, |expr| eval_expression(engine_state, stack, expr))? {
|
||||||
let val = eval_expression(engine_state, stack, expr)?;
|
|
||||||
match val {
|
match val {
|
||||||
Value::Binary { mut val, .. } => output.append(&mut val),
|
Value::Binary { mut val, .. } => output.append(&mut val),
|
||||||
// Explicitly propagate errors instead of dropping them.
|
// Explicitly propagate errors instead of dropping them.
|
||||||
|
@ -105,6 +105,13 @@ impl Command for BytesRemove {
|
|||||||
vec![0x10, 0xAA, 0x10, 0xBB, 0xCC, 0xAA],
|
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 {
|
Example {
|
||||||
description: "Remove all occurrences of find binary in table",
|
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",
|
example: "[[ColA ColB ColC]; [0x[11 12 13] 0x[14 15 16] 0x[17 18 19]]] | bytes remove 0x[11] ColA ColC",
|
||||||
@ -159,8 +166,11 @@ fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
|||||||
}
|
}
|
||||||
// append the remaining thing to result, this can be happening when
|
// append the remaining thing to result, this can be happening when
|
||||||
// we have something to remove and remove_all is False.
|
// we have something to remove and remove_all is False.
|
||||||
let mut remain = input[..left as usize].iter().copied().rev().collect();
|
// check if the left is positive, if it is not, we don't need to append anything.
|
||||||
result.append(&mut remain);
|
if left > 0 {
|
||||||
|
let mut remain = input[..left as usize].iter().copied().rev().collect();
|
||||||
|
result.append(&mut remain);
|
||||||
|
}
|
||||||
result = result.into_iter().rev().collect();
|
result = result.into_iter().rev().collect();
|
||||||
Value::binary(result, span)
|
Value::binary(result, span)
|
||||||
} else {
|
} else {
|
||||||
|
@ -191,6 +191,24 @@ 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_start = evaled_span.start as i64;
|
||||||
let arg_value_name_span_end = evaled_span.end 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! {
|
let record = record! {
|
||||||
"arg_type" => Value::string(arg_type, span),
|
"arg_type" => Value::string(arg_type, span),
|
||||||
"name" => Value::string(arg_value_name, inner_expr.span),
|
"name" => Value::string(arg_value_name, inner_expr.span),
|
||||||
|
@ -4,7 +4,7 @@ use nu_protocol::{
|
|||||||
record, Category, Example, IntoPipelineData, LazyRecord, PipelineData, Record, ShellError,
|
record, Category, Example, IntoPipelineData, LazyRecord, PipelineData, Record, ShellError,
|
||||||
Signature, Span, Type, Value,
|
Signature, Span, Type, Value,
|
||||||
};
|
};
|
||||||
use sysinfo::{Pid, PidExt, ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt};
|
use sysinfo::{MemoryRefreshKind, Pid, ProcessRefreshKind, RefreshKind, System};
|
||||||
const ENV_PATH_SEPARATOR_CHAR: char = {
|
const ENV_PATH_SEPARATOR_CHAR: char = {
|
||||||
#[cfg(target_family = "windows")]
|
#[cfg(target_family = "windows")]
|
||||||
{
|
{
|
||||||
@ -98,8 +98,9 @@ impl LazySystemInfoRecord {
|
|||||||
}
|
}
|
||||||
"system" => {
|
"system" => {
|
||||||
// only get information requested
|
// only get information requested
|
||||||
let system_opt =
|
let system_opt = SystemOpt::from((system_option, || {
|
||||||
SystemOpt::from((system_option, || RefreshKind::new().with_memory()));
|
RefreshKind::new().with_memory(MemoryRefreshKind::everything())
|
||||||
|
}));
|
||||||
|
|
||||||
let system = system_opt.get_system();
|
let system = system_opt.get_system();
|
||||||
|
|
||||||
@ -135,53 +136,66 @@ impl LazySystemInfoRecord {
|
|||||||
"virtual_memory" => Value::filesize(p.virtual_memory() as i64, self.span),
|
"virtual_memory" => Value::filesize(p.virtual_memory() as i64, self.span),
|
||||||
"status" => Value::string(p.status().to_string(), self.span),
|
"status" => Value::string(p.status().to_string(), self.span),
|
||||||
"root" => {
|
"root" => {
|
||||||
if let Some(filename) = p.exe().parent() {
|
if let Some(path) = p.exe().and_then(|p| p.parent()) {
|
||||||
Value::string(filename.to_string_lossy().to_string(), self.span)
|
Value::string(path.to_string_lossy().to_string(), self.span)
|
||||||
} else {
|
} else {
|
||||||
Value::nothing(self.span)
|
Value::nothing(self.span)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cwd" => Value::string(p.cwd().to_string_lossy().to_string(), self.span),
|
"cwd" => {
|
||||||
"exe_path" => Value::string(p.exe().to_string_lossy().to_string(), self.span),
|
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)
|
||||||
|
}
|
||||||
|
},
|
||||||
"command" => Value::string(p.cmd().join(" "), self.span),
|
"command" => Value::string(p.cmd().join(" "), self.span),
|
||||||
"name" => Value::string(p.name().to_string(), self.span),
|
"name" => Value::string(p.name().to_string(), self.span),
|
||||||
"environment" => {
|
"environment" => {
|
||||||
let mut env_rec = Record::new();
|
let mut env_rec = Record::new();
|
||||||
for val in p.environ() {
|
for val in p.environ() {
|
||||||
if let Some((key, value)) = val.split_once('=') {
|
if let Some((key, value)) = val.split_once('=') {
|
||||||
let is_env_var_a_list = {
|
let is_env_var_a_list = {
|
||||||
|
{
|
||||||
|
#[cfg(target_family = "windows")]
|
||||||
{
|
{
|
||||||
#[cfg(target_family = "windows")]
|
key == "Path" || key == "PATHEXT" || key == "PSMODULEPATH" || key == "PSModulePath"
|
||||||
{
|
}
|
||||||
key == "Path" || key == "PATHEXT" || key == "PSMODULEPATH" || key == "PSModulePath"
|
#[cfg(not(target_family = "windows"))]
|
||||||
}
|
{
|
||||||
#[cfg(not(target_family = "windows"))]
|
key == "PATH" || key == "DYLD_FALLBACK_LIBRARY_PATH"
|
||||||
{
|
|
||||||
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,
|
self.span,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
// If we can't get the process information, just return the system information
|
// If we can't get the process information, just return the system information
|
||||||
// only get information requested
|
// only get information requested
|
||||||
let system_opt =
|
let system_opt = SystemOpt::from((system_option, || {
|
||||||
SystemOpt::from((system_option, || RefreshKind::new().with_memory()));
|
RefreshKind::new().with_memory(MemoryRefreshKind::everything())
|
||||||
|
}));
|
||||||
let system = system_opt.get_system();
|
let system = system_opt.get_system();
|
||||||
|
|
||||||
Ok(Value::record(
|
Ok(Value::record(
|
||||||
@ -228,7 +242,7 @@ impl<'a> LazyRecord<'a> for LazySystemInfoRecord {
|
|||||||
.without_disk_usage()
|
.without_disk_usage()
|
||||||
.without_user(),
|
.without_user(),
|
||||||
)
|
)
|
||||||
.with_memory();
|
.with_memory(MemoryRefreshKind::everything());
|
||||||
// only get information requested
|
// only get information requested
|
||||||
let system = System::new_with_specifics(rk);
|
let system = System::new_with_specifics(rk);
|
||||||
|
|
||||||
|
@ -86,6 +86,12 @@ impl Command for Metadata {
|
|||||||
PipelineMetadata {
|
PipelineMetadata {
|
||||||
data_source: DataSource::HtmlThemes,
|
data_source: DataSource::HtmlThemes,
|
||||||
} => record.push("source", Value::string("into html --list", head)),
|
} => 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),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,6 +139,12 @@ fn build_metadata_record(arg: &Value, metadata: Option<&PipelineMetadata>, head:
|
|||||||
PipelineMetadata {
|
PipelineMetadata {
|
||||||
data_source: DataSource::HtmlThemes,
|
data_source: DataSource::HtmlThemes,
|
||||||
} => record.push("source", Value::string("into html --list", head)),
|
} => 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),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
use nu_engine::{current_dir, eval_block, CallExt};
|
use nu_engine::{current_dir, eval_block, CallExt};
|
||||||
|
use nu_path::expand_to_real_path;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::util::BufferedReader;
|
use nu_protocol::util::BufferedReader;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, RawStream, ShellError,
|
Category, DataSource, Example, IntoInterruptiblePipelineData, PipelineData, PipelineMetadata,
|
||||||
Signature, Spanned, SyntaxShape, Type, Value,
|
RawStream, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
|
|
||||||
@ -157,6 +158,7 @@ impl Command for Open {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let buf_reader = BufReader::new(file);
|
let buf_reader = BufReader::new(file);
|
||||||
|
let real_path = expand_to_real_path(path);
|
||||||
|
|
||||||
let file_contents = PipelineData::ExternalStream {
|
let file_contents = PipelineData::ExternalStream {
|
||||||
stdout: Some(RawStream::new(
|
stdout: Some(RawStream::new(
|
||||||
@ -168,7 +170,9 @@ impl Command for Open {
|
|||||||
stderr: None,
|
stderr: None,
|
||||||
exit_code: None,
|
exit_code: None,
|
||||||
span: call_span,
|
span: call_span,
|
||||||
metadata: None,
|
metadata: Some(PipelineMetadata {
|
||||||
|
data_source: DataSource::FilePath(real_path),
|
||||||
|
}),
|
||||||
trim_end_newline: false,
|
trim_end_newline: false,
|
||||||
};
|
};
|
||||||
let exts_opt: Option<Vec<String>> = if raw {
|
let exts_opt: Option<Vec<String>> = if raw {
|
||||||
|
@ -381,11 +381,23 @@ fn rm(
|
|||||||
{
|
{
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
} else if metadata.is_file()
|
} else if metadata.is_symlink() {
|
||||||
|| is_socket
|
// In Windows, symlink pointing to a directory can be removed using
|
||||||
|| is_fifo
|
// std::fs::remove_dir instead of std::fs::remove_file.
|
||||||
|| metadata.file_type().is_symlink()
|
#[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 {
|
||||||
std::fs::remove_file(&f)
|
std::fs::remove_file(&f)
|
||||||
} else {
|
} else {
|
||||||
std::fs::remove_dir_all(&f)
|
std::fs::remove_dir_all(&f)
|
||||||
|
@ -4,8 +4,8 @@ use nu_path::expand_path_with;
|
|||||||
use nu_protocol::ast::{Call, Expr, Expression};
|
use nu_protocol::ast::{Call, Expr, Expression};
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, PipelineData, RawStream, ShellError, Signature, Span, Spanned, SyntaxShape,
|
Category, DataSource, Example, PipelineData, PipelineMetadata, RawStream, ShellError,
|
||||||
Type, Value,
|
Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
@ -149,9 +149,42 @@ impl Command for Save {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PipelineData::ListStream(ls, _)
|
PipelineData::ListStream(ls, pipeline_metadata)
|
||||||
if raw || prepare_path(&path, append, force)?.0.extension().is_none() =>
|
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(
|
let (mut file, _) = get_files(
|
||||||
&path,
|
&path,
|
||||||
stderr_path.as_ref(),
|
stderr_path.as_ref(),
|
||||||
@ -340,10 +373,7 @@ fn prepare_path(
|
|||||||
|
|
||||||
fn open_file(path: &Path, span: Span, append: bool) -> Result<File, ShellError> {
|
fn open_file(path: &Path, span: Span, append: bool) -> Result<File, ShellError> {
|
||||||
let file = match (append, path.exists()) {
|
let file = match (append, path.exists()) {
|
||||||
(true, true) => std::fs::OpenOptions::new()
|
(true, true) => std::fs::OpenOptions::new().append(true).open(path),
|
||||||
.write(true)
|
|
||||||
.append(true)
|
|
||||||
.open(path),
|
|
||||||
_ => std::fs::File::create(path),
|
_ => std::fs::File::create(path),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,10 +34,10 @@ impl Command for ToXml {
|
|||||||
fn extra_usage(&self) -> &str {
|
fn extra_usage(&self) -> &str {
|
||||||
r#"Every XML entry is represented via a record with tag, attribute and content fields.
|
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:
|
To represent different types of entries different values must be written to this fields:
|
||||||
1. Tag entry: `{tag: <tag name> attrs: {<attr name>: "<string value>" ...} content: [<entries>]}`
|
1. Tag entry: `{tag: <tag name> attributes: {<attr name>: "<string value>" ...} content: [<entries>]}`
|
||||||
2. Comment entry: `{tag: '!' attrs: null content: "<comment string>"}`
|
2. Comment entry: `{tag: '!' attributes: null content: "<comment string>"}`
|
||||||
3. Processing instruction (PI): `{tag: '?<pi name>' attrs: null content: "<pi content string>"}`
|
3. Processing instruction (PI): `{tag: '?<pi name>' attributes: null content: "<pi content string>"}`
|
||||||
4. Text: `{tag: null attrs: null content: "<text>"}`. Or as plain `<text>` instead of record.
|
4. Text: `{tag: null attributes: null content: "<text>"}`. Or as plain `<text>` instead of record.
|
||||||
|
|
||||||
Additionally any field which is: empty record, empty list or null, can be omitted."#
|
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![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Outputs an XML string representing the contents of this table",
|
description: "Outputs an XML string representing the contents of this table",
|
||||||
example: r#"{tag: note attributes: {} content : [{tag: remember attributes: {} content : [{tag: null attrs: null content : Event}]}]} | to xml"#,
|
example: r#"{tag: note attributes: {} content : [{tag: remember attributes: {} content : [{tag: null attributes: null content : Event}]}]} | to xml"#,
|
||||||
result: Some(Value::test_string(
|
result: Some(Value::test_string(
|
||||||
"<note><remember>Event</remember></note>",
|
"<note><remember>Event</remember></note>",
|
||||||
)),
|
)),
|
||||||
@ -110,6 +110,17 @@ fn to_xml_entry<W: Write>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Value::Record { val: record, .. } = &entry {
|
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
|
// If key is not found it is assumed to be nothing. This way
|
||||||
// user can write a tag like {tag: a content: [...]} instead
|
// user can write a tag like {tag: a content: [...]} instead
|
||||||
// of longer {tag: a attributes: {} content: [...]}
|
// of longer {tag: a attributes: {} content: [...]}
|
||||||
@ -144,7 +155,12 @@ fn to_xml_entry<W: Write>(
|
|||||||
(Value::String { val: tag_name, .. }, attrs, children) => to_tag_like(
|
(Value::String { val: tag_name, .. }, attrs, children) => to_tag_like(
|
||||||
entry_span, tag_name, tag_span, attrs, children, top_level, writer,
|
entry_span, tag_name, tag_span, attrs, children, top_level, writer,
|
||||||
),
|
),
|
||||||
_ => Ok(()),
|
_ => Err(ShellError::CantConvert {
|
||||||
|
to_type: "XML".into(),
|
||||||
|
from_type: "record".into(),
|
||||||
|
span: entry_span,
|
||||||
|
help: Some("Tag missing or is not a string".into()),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(ShellError::CantConvert {
|
Err(ShellError::CantConvert {
|
||||||
@ -156,6 +172,14 @@ 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.
|
/// Convert record to tag-like entry: tag, PI, comment.
|
||||||
fn to_tag_like<W: Write>(
|
fn to_tag_like<W: Write>(
|
||||||
entry_span: Span,
|
entry_span: Span,
|
||||||
|
@ -23,6 +23,7 @@ mod random;
|
|||||||
mod removed;
|
mod removed;
|
||||||
mod shells;
|
mod shells;
|
||||||
mod sort_utils;
|
mod sort_utils;
|
||||||
|
#[cfg(feature = "sqlite")]
|
||||||
mod stor;
|
mod stor;
|
||||||
mod strings;
|
mod strings;
|
||||||
mod system;
|
mod system;
|
||||||
|
@ -5,7 +5,8 @@ use base64::{alphabet, Engine};
|
|||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{EngineState, Stack};
|
use nu_protocol::engine::{EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
record, BufferedReader, IntoPipelineData, PipelineData, RawStream, ShellError, Span, Value,
|
record, BufferedReader, IntoPipelineData, PipelineData, RawStream, ShellError, Span, Spanned,
|
||||||
|
Value,
|
||||||
};
|
};
|
||||||
use ureq::{Error, ErrorKind, Request, Response};
|
use ureq::{Error, ErrorKind, Request, Response};
|
||||||
|
|
||||||
@ -26,29 +27,45 @@ pub enum BodyType {
|
|||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only panics if the user agent is invalid but we define it statically so either
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
// it always or never fails
|
pub enum RedirectMode {
|
||||||
|
Follow,
|
||||||
|
Error,
|
||||||
|
Manual,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn http_client(
|
pub fn http_client(
|
||||||
allow_insecure: bool,
|
allow_insecure: bool,
|
||||||
|
redirect_mode: RedirectMode,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
) -> ureq::Agent {
|
) -> Result<ureq::Agent, ShellError> {
|
||||||
let tls = native_tls::TlsConnector::builder()
|
let tls = native_tls::TlsConnector::builder()
|
||||||
.danger_accept_invalid_certs(allow_insecure)
|
.danger_accept_invalid_certs(allow_insecure)
|
||||||
.build()
|
.build()
|
||||||
.expect("Failed to build network tls");
|
.map_err(|e| ShellError::GenericError {
|
||||||
|
error: format!("Failed to build network tls: {}", e),
|
||||||
|
msg: String::new(),
|
||||||
|
span: None,
|
||||||
|
help: None,
|
||||||
|
inner: vec![],
|
||||||
|
})?;
|
||||||
|
|
||||||
let mut agent_builder = ureq::builder()
|
let mut agent_builder = ureq::builder()
|
||||||
.user_agent("nushell")
|
.user_agent("nushell")
|
||||||
.tls_connector(std::sync::Arc::new(tls));
|
.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 Some(http_proxy) = retrieve_http_proxy_from_env(engine_state, stack) {
|
||||||
if let Ok(proxy) = ureq::Proxy::new(http_proxy) {
|
if let Ok(proxy) = ureq::Proxy::new(http_proxy) {
|
||||||
agent_builder = agent_builder.proxy(proxy);
|
agent_builder = agent_builder.proxy(proxy);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
agent_builder.build()
|
Ok(agent_builder.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn http_parse_url(
|
pub fn http_parse_url(
|
||||||
@ -68,6 +85,18 @@ pub fn http_parse_url(
|
|||||||
Ok((requested_url, 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(
|
pub fn response_to_buffer(
|
||||||
response: Response,
|
response: Response,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -452,6 +481,26 @@ 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(
|
fn request_handle_response_content(
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
|
@ -2,12 +2,13 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::network::http::client::{
|
use crate::network::http::client::{
|
||||||
http_client, http_parse_url, request_add_authorization_header, request_add_custom_headers,
|
check_response_redirection, http_client, http_parse_redirect_mode, http_parse_url,
|
||||||
request_handle_response, request_set_timeout, send_request,
|
request_add_authorization_header, request_add_custom_headers, request_handle_response,
|
||||||
|
request_set_timeout, send_request,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::client::RequestFlags;
|
use super::client::RequestFlags;
|
||||||
@ -79,6 +80,11 @@ impl Command for SubCommand {
|
|||||||
"allow-errors",
|
"allow-errors",
|
||||||
"do not fail if the server returns an error code",
|
"do not fail if the server returns an error code",
|
||||||
Some('e'),
|
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()
|
.filter()
|
||||||
.category(Category::Network)
|
.category(Category::Network)
|
||||||
@ -150,6 +156,7 @@ struct Arguments {
|
|||||||
timeout: Option<Value>,
|
timeout: Option<Value>,
|
||||||
full: bool,
|
full: bool,
|
||||||
allow_errors: bool,
|
allow_errors: bool,
|
||||||
|
redirect: Option<Spanned<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_delete(
|
fn run_delete(
|
||||||
@ -170,6 +177,7 @@ fn run_delete(
|
|||||||
timeout: call.get_flag(engine_state, stack, "max-time")?,
|
timeout: call.get_flag(engine_state, stack, "max-time")?,
|
||||||
full: call.has_flag("full"),
|
full: call.has_flag("full"),
|
||||||
allow_errors: call.has_flag("allow-errors"),
|
allow_errors: call.has_flag("allow-errors"),
|
||||||
|
redirect: call.get_flag(engine_state, stack, "redirect-mode")?,
|
||||||
};
|
};
|
||||||
|
|
||||||
helper(engine_state, stack, call, args)
|
helper(engine_state, stack, call, args)
|
||||||
@ -186,8 +194,9 @@ fn helper(
|
|||||||
let span = args.url.span();
|
let span = args.url.span();
|
||||||
let ctrl_c = engine_state.ctrlc.clone();
|
let ctrl_c = engine_state.ctrlc.clone();
|
||||||
let (requested_url, _) = http_parse_url(call, span, args.url)?;
|
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, engine_state, stack);
|
let client = http_client(args.insecure, redirect_mode, engine_state, stack)?;
|
||||||
let mut request = client.delete(&requested_url);
|
let mut request = client.delete(&requested_url);
|
||||||
|
|
||||||
request = request_set_timeout(args.timeout, request)?;
|
request = request_set_timeout(args.timeout, request)?;
|
||||||
@ -202,6 +211,7 @@ fn helper(
|
|||||||
allow_errors: args.allow_errors,
|
allow_errors: args.allow_errors,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
check_response_redirection(redirect_mode, span, &response)?;
|
||||||
request_handle_response(
|
request_handle_response(
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
|
@ -2,16 +2,15 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::network::http::client::{
|
use crate::network::http::client::{
|
||||||
http_client, http_parse_url, request_add_authorization_header, request_add_custom_headers,
|
check_response_redirection, http_client, http_parse_redirect_mode, http_parse_url,
|
||||||
request_handle_response, request_set_timeout, send_request,
|
request_add_authorization_header, request_add_custom_headers, request_handle_response,
|
||||||
|
request_set_timeout, send_request, RequestFlags,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::client::RequestFlags;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SubCommand;
|
pub struct SubCommand;
|
||||||
|
|
||||||
@ -73,6 +72,12 @@ impl Command for SubCommand {
|
|||||||
"do not fail if the server returns an error code",
|
"do not fail if the server returns an error code",
|
||||||
Some('e'),
|
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()
|
.filter()
|
||||||
.category(Category::Network)
|
.category(Category::Network)
|
||||||
}
|
}
|
||||||
@ -137,6 +142,7 @@ struct Arguments {
|
|||||||
timeout: Option<Value>,
|
timeout: Option<Value>,
|
||||||
full: bool,
|
full: bool,
|
||||||
allow_errors: bool,
|
allow_errors: bool,
|
||||||
|
redirect: Option<Spanned<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_get(
|
fn run_get(
|
||||||
@ -155,6 +161,7 @@ fn run_get(
|
|||||||
timeout: call.get_flag(engine_state, stack, "max-time")?,
|
timeout: call.get_flag(engine_state, stack, "max-time")?,
|
||||||
full: call.has_flag("full"),
|
full: call.has_flag("full"),
|
||||||
allow_errors: call.has_flag("allow-errors"),
|
allow_errors: call.has_flag("allow-errors"),
|
||||||
|
redirect: call.get_flag(engine_state, stack, "redirect-mode")?,
|
||||||
};
|
};
|
||||||
helper(engine_state, stack, call, args)
|
helper(engine_state, stack, call, args)
|
||||||
}
|
}
|
||||||
@ -170,8 +177,9 @@ fn helper(
|
|||||||
let span = args.url.span();
|
let span = args.url.span();
|
||||||
let ctrl_c = engine_state.ctrlc.clone();
|
let ctrl_c = engine_state.ctrlc.clone();
|
||||||
let (requested_url, _) = http_parse_url(call, span, args.url)?;
|
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, engine_state, stack);
|
let client = http_client(args.insecure, redirect_mode, engine_state, stack)?;
|
||||||
let mut request = client.get(&requested_url);
|
let mut request = client.get(&requested_url);
|
||||||
|
|
||||||
request = request_set_timeout(args.timeout, request)?;
|
request = request_set_timeout(args.timeout, request)?;
|
||||||
@ -186,6 +194,7 @@ fn helper(
|
|||||||
allow_errors: args.allow_errors,
|
allow_errors: args.allow_errors,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
check_response_redirection(redirect_mode, span, &response)?;
|
||||||
request_handle_response(
|
request_handle_response(
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
|
@ -5,12 +5,13 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::network::http::client::{
|
use crate::network::http::client::{
|
||||||
http_client, http_parse_url, request_add_authorization_header, request_add_custom_headers,
|
check_response_redirection, http_client, http_parse_redirect_mode, http_parse_url,
|
||||||
request_handle_response_headers, request_set_timeout, send_request,
|
request_add_authorization_header, request_add_custom_headers, request_handle_response_headers,
|
||||||
|
request_set_timeout, send_request,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -58,6 +59,11 @@ impl Command for SubCommand {
|
|||||||
"insecure",
|
"insecure",
|
||||||
"allow insecure server connections when using SSL",
|
"allow insecure server connections when using SSL",
|
||||||
Some('k'),
|
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()
|
.filter()
|
||||||
.category(Category::Network)
|
.category(Category::Network)
|
||||||
@ -114,6 +120,7 @@ struct Arguments {
|
|||||||
user: Option<String>,
|
user: Option<String>,
|
||||||
password: Option<String>,
|
password: Option<String>,
|
||||||
timeout: Option<Value>,
|
timeout: Option<Value>,
|
||||||
|
redirect: Option<Spanned<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_head(
|
fn run_head(
|
||||||
@ -129,6 +136,7 @@ fn run_head(
|
|||||||
user: call.get_flag(engine_state, stack, "user")?,
|
user: call.get_flag(engine_state, stack, "user")?,
|
||||||
password: call.get_flag(engine_state, stack, "password")?,
|
password: call.get_flag(engine_state, stack, "password")?,
|
||||||
timeout: call.get_flag(engine_state, stack, "max-time")?,
|
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();
|
let ctrl_c = engine_state.ctrlc.clone();
|
||||||
|
|
||||||
@ -146,8 +154,9 @@ fn helper(
|
|||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let span = args.url.span();
|
let span = args.url.span();
|
||||||
let (requested_url, _) = http_parse_url(call, span, args.url)?;
|
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, engine_state, stack);
|
let client = http_client(args.insecure, redirect_mode, engine_state, stack)?;
|
||||||
let mut request = client.head(&requested_url);
|
let mut request = client.head(&requested_url);
|
||||||
|
|
||||||
request = request_set_timeout(args.timeout, request)?;
|
request = request_set_timeout(args.timeout, request)?;
|
||||||
@ -155,6 +164,7 @@ fn helper(
|
|||||||
request = request_add_custom_headers(args.headers, request)?;
|
request = request_add_custom_headers(args.headers, request)?;
|
||||||
|
|
||||||
let response = send_request(request, None, None, ctrlc);
|
let response = send_request(request, None, None, ctrlc);
|
||||||
|
check_response_redirection(redirect_mode, span, &response)?;
|
||||||
request_handle_response_headers(span, response)
|
request_handle_response_headers(span, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ use crate::network::http::client::{
|
|||||||
request_handle_response, request_set_timeout, send_request,
|
request_handle_response, request_set_timeout, send_request,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::client::RequestFlags;
|
use super::client::{RedirectMode, RequestFlags};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SubCommand;
|
pub struct SubCommand;
|
||||||
@ -160,7 +160,7 @@ fn helper(
|
|||||||
let ctrl_c = engine_state.ctrlc.clone();
|
let ctrl_c = engine_state.ctrlc.clone();
|
||||||
let (requested_url, _) = http_parse_url(call, span, args.url)?;
|
let (requested_url, _) = http_parse_url(call, span, args.url)?;
|
||||||
|
|
||||||
let client = http_client(args.insecure, engine_state, stack);
|
let client = http_client(args.insecure, RedirectMode::Follow, engine_state, stack)?;
|
||||||
let mut request = client.request("OPTIONS", &requested_url);
|
let mut request = client.request("OPTIONS", &requested_url);
|
||||||
|
|
||||||
request = request_set_timeout(args.timeout, request)?;
|
request = request_set_timeout(args.timeout, request)?;
|
||||||
|
@ -2,12 +2,13 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::network::http::client::{
|
use crate::network::http::client::{
|
||||||
http_client, http_parse_url, request_add_authorization_header, request_add_custom_headers,
|
check_response_redirection, http_client, http_parse_redirect_mode, http_parse_url,
|
||||||
request_handle_response, request_set_timeout, send_request,
|
request_add_authorization_header, request_add_custom_headers, request_handle_response,
|
||||||
|
request_set_timeout, send_request,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::client::RequestFlags;
|
use super::client::RequestFlags;
|
||||||
@ -75,6 +76,11 @@ impl Command for SubCommand {
|
|||||||
"allow-errors",
|
"allow-errors",
|
||||||
"do not fail if the server returns an error code",
|
"do not fail if the server returns an error code",
|
||||||
Some('e'),
|
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()
|
.filter()
|
||||||
.category(Category::Network)
|
.category(Category::Network)
|
||||||
@ -142,6 +148,7 @@ struct Arguments {
|
|||||||
timeout: Option<Value>,
|
timeout: Option<Value>,
|
||||||
full: bool,
|
full: bool,
|
||||||
allow_errors: bool,
|
allow_errors: bool,
|
||||||
|
redirect: Option<Spanned<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_patch(
|
fn run_patch(
|
||||||
@ -162,6 +169,7 @@ fn run_patch(
|
|||||||
timeout: call.get_flag(engine_state, stack, "max-time")?,
|
timeout: call.get_flag(engine_state, stack, "max-time")?,
|
||||||
full: call.has_flag("full"),
|
full: call.has_flag("full"),
|
||||||
allow_errors: call.has_flag("allow-errors"),
|
allow_errors: call.has_flag("allow-errors"),
|
||||||
|
redirect: call.get_flag(engine_state, stack, "redirect-mode")?,
|
||||||
};
|
};
|
||||||
|
|
||||||
helper(engine_state, stack, call, args)
|
helper(engine_state, stack, call, args)
|
||||||
@ -178,8 +186,9 @@ fn helper(
|
|||||||
let span = args.url.span();
|
let span = args.url.span();
|
||||||
let ctrl_c = engine_state.ctrlc.clone();
|
let ctrl_c = engine_state.ctrlc.clone();
|
||||||
let (requested_url, _) = http_parse_url(call, span, args.url)?;
|
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, engine_state, stack);
|
let client = http_client(args.insecure, redirect_mode, engine_state, stack)?;
|
||||||
let mut request = client.patch(&requested_url);
|
let mut request = client.patch(&requested_url);
|
||||||
|
|
||||||
request = request_set_timeout(args.timeout, request)?;
|
request = request_set_timeout(args.timeout, request)?;
|
||||||
@ -194,6 +203,7 @@ fn helper(
|
|||||||
allow_errors: args.allow_errors,
|
allow_errors: args.allow_errors,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
check_response_redirection(redirect_mode, span, &response)?;
|
||||||
request_handle_response(
|
request_handle_response(
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
|
@ -2,12 +2,13 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::network::http::client::{
|
use crate::network::http::client::{
|
||||||
http_client, http_parse_url, request_add_authorization_header, request_add_custom_headers,
|
check_response_redirection, http_client, http_parse_redirect_mode, http_parse_url,
|
||||||
request_handle_response, request_set_timeout, send_request,
|
request_add_authorization_header, request_add_custom_headers, request_handle_response,
|
||||||
|
request_set_timeout, send_request,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::client::RequestFlags;
|
use super::client::RequestFlags;
|
||||||
@ -75,6 +76,11 @@ impl Command for SubCommand {
|
|||||||
"allow-errors",
|
"allow-errors",
|
||||||
"do not fail if the server returns an error code",
|
"do not fail if the server returns an error code",
|
||||||
Some('e'),
|
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()
|
.filter()
|
||||||
.category(Category::Network)
|
.category(Category::Network)
|
||||||
@ -140,6 +146,7 @@ struct Arguments {
|
|||||||
timeout: Option<Value>,
|
timeout: Option<Value>,
|
||||||
full: bool,
|
full: bool,
|
||||||
allow_errors: bool,
|
allow_errors: bool,
|
||||||
|
redirect: Option<Spanned<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_post(
|
fn run_post(
|
||||||
@ -160,6 +167,7 @@ fn run_post(
|
|||||||
timeout: call.get_flag(engine_state, stack, "max-time")?,
|
timeout: call.get_flag(engine_state, stack, "max-time")?,
|
||||||
full: call.has_flag("full"),
|
full: call.has_flag("full"),
|
||||||
allow_errors: call.has_flag("allow-errors"),
|
allow_errors: call.has_flag("allow-errors"),
|
||||||
|
redirect: call.get_flag(engine_state, stack, "redirect-mode")?,
|
||||||
};
|
};
|
||||||
|
|
||||||
helper(engine_state, stack, call, args)
|
helper(engine_state, stack, call, args)
|
||||||
@ -176,8 +184,9 @@ fn helper(
|
|||||||
let span = args.url.span();
|
let span = args.url.span();
|
||||||
let ctrl_c = engine_state.ctrlc.clone();
|
let ctrl_c = engine_state.ctrlc.clone();
|
||||||
let (requested_url, _) = http_parse_url(call, span, args.url)?;
|
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, engine_state, stack);
|
let client = http_client(args.insecure, redirect_mode, engine_state, stack)?;
|
||||||
let mut request = client.post(&requested_url);
|
let mut request = client.post(&requested_url);
|
||||||
|
|
||||||
request = request_set_timeout(args.timeout, request)?;
|
request = request_set_timeout(args.timeout, request)?;
|
||||||
@ -192,6 +201,7 @@ fn helper(
|
|||||||
allow_errors: args.allow_errors,
|
allow_errors: args.allow_errors,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
check_response_redirection(redirect_mode, span, &response)?;
|
||||||
request_handle_response(
|
request_handle_response(
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
|
@ -2,12 +2,13 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::network::http::client::{
|
use crate::network::http::client::{
|
||||||
http_client, http_parse_url, request_add_authorization_header, request_add_custom_headers,
|
check_response_redirection, http_client, http_parse_redirect_mode, http_parse_url,
|
||||||
request_handle_response, request_set_timeout, send_request,
|
request_add_authorization_header, request_add_custom_headers, request_handle_response,
|
||||||
|
request_set_timeout, send_request,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::client::RequestFlags;
|
use super::client::RequestFlags;
|
||||||
@ -75,6 +76,11 @@ impl Command for SubCommand {
|
|||||||
"allow-errors",
|
"allow-errors",
|
||||||
"do not fail if the server returns an error code",
|
"do not fail if the server returns an error code",
|
||||||
Some('e'),
|
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()
|
.filter()
|
||||||
.category(Category::Network)
|
.category(Category::Network)
|
||||||
@ -140,6 +146,7 @@ struct Arguments {
|
|||||||
timeout: Option<Value>,
|
timeout: Option<Value>,
|
||||||
full: bool,
|
full: bool,
|
||||||
allow_errors: bool,
|
allow_errors: bool,
|
||||||
|
redirect: Option<Spanned<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_put(
|
fn run_put(
|
||||||
@ -160,6 +167,7 @@ fn run_put(
|
|||||||
timeout: call.get_flag(engine_state, stack, "max-time")?,
|
timeout: call.get_flag(engine_state, stack, "max-time")?,
|
||||||
full: call.has_flag("full"),
|
full: call.has_flag("full"),
|
||||||
allow_errors: call.has_flag("allow-errors"),
|
allow_errors: call.has_flag("allow-errors"),
|
||||||
|
redirect: call.get_flag(engine_state, stack, "redirect-mode")?,
|
||||||
};
|
};
|
||||||
|
|
||||||
helper(engine_state, stack, call, args)
|
helper(engine_state, stack, call, args)
|
||||||
@ -176,8 +184,9 @@ fn helper(
|
|||||||
let span = args.url.span();
|
let span = args.url.span();
|
||||||
let ctrl_c = engine_state.ctrlc.clone();
|
let ctrl_c = engine_state.ctrlc.clone();
|
||||||
let (requested_url, _) = http_parse_url(call, span, args.url)?;
|
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, engine_state, stack);
|
let client = http_client(args.insecure, redirect_mode, engine_state, stack)?;
|
||||||
let mut request = client.put(&requested_url);
|
let mut request = client.put(&requested_url);
|
||||||
|
|
||||||
request = request_set_timeout(args.timeout, request)?;
|
request = request_set_timeout(args.timeout, request)?;
|
||||||
@ -192,6 +201,7 @@ fn helper(
|
|||||||
allow_errors: args.allow_errors,
|
allow_errors: args.allow_errors,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
check_response_redirection(redirect_mode, span, &response)?;
|
||||||
request_handle_response(
|
request_handle_response(
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
|
@ -1,35 +1,19 @@
|
|||||||
#[cfg(feature = "sqlite")]
|
|
||||||
mod create;
|
mod create;
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
mod delete;
|
mod delete;
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
mod export;
|
mod export;
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
mod import;
|
mod import;
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
mod insert;
|
mod insert;
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
mod open;
|
mod open;
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
mod reset;
|
mod reset;
|
||||||
mod stor_;
|
mod stor_;
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
mod update;
|
mod update;
|
||||||
|
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
pub use create::StorCreate;
|
pub use create::StorCreate;
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
pub use delete::StorDelete;
|
pub use delete::StorDelete;
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
pub use export::StorExport;
|
pub use export::StorExport;
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
pub use import::StorImport;
|
pub use import::StorImport;
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
pub use insert::StorInsert;
|
pub use insert::StorInsert;
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
pub use open::StorOpen;
|
pub use open::StorOpen;
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
pub use reset::StorReset;
|
pub use reset::StorReset;
|
||||||
pub use stor_::Stor;
|
pub use stor_::Stor;
|
||||||
#[cfg(feature = "sqlite")]
|
|
||||||
pub use update::StorUpdate;
|
pub use update::StorUpdate;
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
use nu_cmd_base::hook::eval_hook;
|
use nu_cmd_base::hook::eval_hook;
|
||||||
use nu_engine::env_to_strings;
|
use nu_engine::env_to_strings;
|
||||||
|
use nu_engine::eval_expression;
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, Expr, Expression},
|
ast::{Call, Expr},
|
||||||
did_you_mean,
|
did_you_mean,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, ListStream, PipelineData, RawStream, ShellError, Signature, Span, Spanned,
|
Category, Example, ListStream, PipelineData, RawStream, ShellError, Signature, Span, Spanned,
|
||||||
@ -113,7 +114,6 @@ pub fn create_external_command(
|
|||||||
trim_end_newline: bool,
|
trim_end_newline: bool,
|
||||||
) -> Result<ExternalCommand, ShellError> {
|
) -> Result<ExternalCommand, ShellError> {
|
||||||
let name: Spanned<String> = call.req(engine_state, stack, 0)?;
|
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
|
// Translate environment variables from Values to Strings
|
||||||
let env_vars_str = env_to_strings(engine_state, stack)?;
|
let env_vars_str = env_to_strings(engine_state, stack)?;
|
||||||
@ -132,11 +132,24 @@ pub fn create_external_command(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut spanned_args = vec![];
|
let mut spanned_args = vec![];
|
||||||
let args_expr: Vec<Expression> = call.positional_iter().skip(1).cloned().collect();
|
|
||||||
let mut arg_keep_raw = vec![];
|
let mut arg_keep_raw = vec![];
|
||||||
for (one_arg, one_arg_expr) in args.into_iter().zip(args_expr) {
|
for (arg, spread) in call.rest_iter(1) {
|
||||||
match one_arg {
|
// 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)? {
|
||||||
Value::List { vals, .. } => {
|
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.
|
// turn all the strings in the array into params.
|
||||||
// Example: one_arg may be something like ["ls" "-a"]
|
// Example: one_arg may be something like ["ls" "-a"]
|
||||||
// convert it to "ls" "-a"
|
// convert it to "ls" "-a"
|
||||||
@ -147,15 +160,20 @@ pub fn create_external_command(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val => {
|
val => {
|
||||||
spanned_args.push(value_as_spanned(val)?);
|
if spread {
|
||||||
match one_arg_expr.expr {
|
return Err(ShellError::CannotSpreadAsList { span: arg.span });
|
||||||
// refer to `parse_dollar_expr` function
|
} else {
|
||||||
// the expression type of $variable_name, $"($variable_name)"
|
spanned_args.push(value_as_spanned(val)?);
|
||||||
// will be Expr::StringInterpolation, Expr::FullCellPath
|
match arg.expr {
|
||||||
Expr::StringInterpolation(_) | Expr::FullCellPath(_) => arg_keep_raw.push(true),
|
// refer to `parse_dollar_expr` function
|
||||||
_ => arg_keep_raw.push(false),
|
// 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),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
{}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use nu_protocol::{
|
|||||||
};
|
};
|
||||||
use std::time::{Duration, UNIX_EPOCH};
|
use std::time::{Duration, UNIX_EPOCH};
|
||||||
use sysinfo::{
|
use sysinfo::{
|
||||||
ComponentExt, CpuExt, CpuRefreshKind, DiskExt, NetworkExt, System, SystemExt, UserExt,
|
Components, CpuRefreshKind, Disks, Networks, System, Users, MINIMUM_CPU_UPDATE_INTERVAL,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -106,14 +106,12 @@ pub fn trim_cstyle_null(s: String) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn disks(span: Span) -> Value {
|
pub fn disks(span: Span) -> Value {
|
||||||
let mut sys = System::new();
|
let disks = Disks::new_with_refreshed_list();
|
||||||
sys.refresh_disks();
|
|
||||||
sys.refresh_disks_list();
|
|
||||||
|
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
for disk in sys.disks() {
|
for disk in disks.list() {
|
||||||
let device = trim_cstyle_null(disk.name().to_string_lossy().to_string());
|
let device = trim_cstyle_null(disk.name().to_string_lossy().to_string());
|
||||||
let typ = trim_cstyle_null(String::from_utf8_lossy(disk.file_system()).to_string());
|
let typ = trim_cstyle_null(disk.file_system().to_string_lossy().to_string());
|
||||||
|
|
||||||
let record = record! {
|
let record = record! {
|
||||||
"device" => Value::string(device, span),
|
"device" => Value::string(device, span),
|
||||||
@ -131,12 +129,10 @@ pub fn disks(span: Span) -> Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn net(span: Span) -> Value {
|
pub fn net(span: Span) -> Value {
|
||||||
let mut sys = System::new();
|
let networks = Networks::new_with_refreshed_list();
|
||||||
sys.refresh_networks();
|
|
||||||
sys.refresh_networks_list();
|
|
||||||
|
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
for (iface, data) in sys.networks() {
|
for (iface, data) in networks.list() {
|
||||||
let record = record! {
|
let record = record! {
|
||||||
"name" => Value::string(trim_cstyle_null(iface.to_string()), span),
|
"name" => Value::string(trim_cstyle_null(iface.to_string()), span),
|
||||||
"sent" => Value::filesize(data.total_transmitted() as i64, span),
|
"sent" => Value::filesize(data.total_transmitted() as i64, span),
|
||||||
@ -154,7 +150,7 @@ pub fn cpu(span: Span) -> Value {
|
|||||||
// We must refresh the CPU twice a while apart to get valid usage data.
|
// 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
|
// 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
|
// that gives poor results (error of ~5%). Decided to wait 2x that long, somewhat arbitrarily
|
||||||
std::thread::sleep(System::MINIMUM_CPU_UPDATE_INTERVAL * 2);
|
std::thread::sleep(MINIMUM_CPU_UPDATE_INTERVAL * 2);
|
||||||
sys.refresh_cpu_specifics(CpuRefreshKind::new().with_cpu_usage());
|
sys.refresh_cpu_specifics(CpuRefreshKind::new().with_cpu_usage());
|
||||||
|
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
@ -163,7 +159,7 @@ pub fn cpu(span: Span) -> Value {
|
|||||||
// Round to 1DP (chosen somewhat arbitrarily) so people aren't misled by high-precision floats.
|
// 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 rounded_usage = (cpu.cpu_usage() * 10.0).round() / 10.0;
|
||||||
|
|
||||||
let load_avg = sys.load_average();
|
let load_avg = System::load_average();
|
||||||
let load_avg = trim_cstyle_null(format!(
|
let load_avg = trim_cstyle_null(format!(
|
||||||
"{:.2}, {:.2}, {:.2}",
|
"{:.2}, {:.2}, {:.2}",
|
||||||
load_avg.one, load_avg.five, load_avg.fifteen
|
load_avg.one, load_avg.five, load_avg.fifteen
|
||||||
@ -211,42 +207,39 @@ pub fn mem(span: Span) -> Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn host(span: Span) -> Value {
|
pub fn host(span: Span) -> Value {
|
||||||
let mut sys = System::new();
|
|
||||||
sys.refresh_users_list();
|
|
||||||
|
|
||||||
let mut record = Record::new();
|
let mut record = Record::new();
|
||||||
|
|
||||||
if let Some(name) = sys.name() {
|
if let Some(name) = System::name() {
|
||||||
record.push("name", Value::string(trim_cstyle_null(name), span));
|
record.push("name", Value::string(trim_cstyle_null(name), span));
|
||||||
}
|
}
|
||||||
if let Some(version) = sys.os_version() {
|
if let Some(version) = System::os_version() {
|
||||||
record.push("os_version", Value::string(trim_cstyle_null(version), span));
|
record.push("os_version", Value::string(trim_cstyle_null(version), span));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(long_version) = sys.long_os_version() {
|
if let Some(long_version) = System::long_os_version() {
|
||||||
record.push(
|
record.push(
|
||||||
"long_os_version",
|
"long_os_version",
|
||||||
Value::string(trim_cstyle_null(long_version), span),
|
Value::string(trim_cstyle_null(long_version), span),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(version) = sys.kernel_version() {
|
if let Some(version) = System::kernel_version() {
|
||||||
record.push(
|
record.push(
|
||||||
"kernel_version",
|
"kernel_version",
|
||||||
Value::string(trim_cstyle_null(version), span),
|
Value::string(trim_cstyle_null(version), span),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if let Some(hostname) = sys.host_name() {
|
if let Some(hostname) = System::host_name() {
|
||||||
record.push("hostname", Value::string(trim_cstyle_null(hostname), span));
|
record.push("hostname", Value::string(trim_cstyle_null(hostname), span));
|
||||||
}
|
}
|
||||||
|
|
||||||
record.push(
|
record.push(
|
||||||
"uptime",
|
"uptime",
|
||||||
Value::duration(1000000000 * sys.uptime() as i64, span),
|
Value::duration(1000000000 * System::uptime() as i64, span),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Creates a new SystemTime from the specified number of whole seconds
|
// Creates a new SystemTime from the specified number of whole seconds
|
||||||
let d = UNIX_EPOCH + Duration::from_secs(sys.boot_time());
|
let d = UNIX_EPOCH + Duration::from_secs(System::boot_time());
|
||||||
// Create DateTime from SystemTime
|
// Create DateTime from SystemTime
|
||||||
let datetime = DateTime::<Local>::from(d);
|
let datetime = DateTime::<Local>::from(d);
|
||||||
// Convert to local time and then rfc3339
|
// Convert to local time and then rfc3339
|
||||||
@ -254,11 +247,16 @@ pub fn host(span: Span) -> Value {
|
|||||||
|
|
||||||
record.push("boot_time", Value::string(timestamp_str, span));
|
record.push("boot_time", Value::string(timestamp_str, span));
|
||||||
|
|
||||||
let mut users = vec![];
|
let users = Users::new_with_refreshed_list();
|
||||||
for user in sys.users() {
|
|
||||||
|
let mut users_list = vec![];
|
||||||
|
for user in users.list() {
|
||||||
let mut groups = vec![];
|
let mut groups = vec![];
|
||||||
for group in user.groups() {
|
for group in user.groups() {
|
||||||
groups.push(Value::string(trim_cstyle_null(group.to_string()), span));
|
groups.push(Value::string(
|
||||||
|
trim_cstyle_null(group.name().to_string()),
|
||||||
|
span,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let record = record! {
|
let record = record! {
|
||||||
@ -266,24 +264,22 @@ pub fn host(span: Span) -> Value {
|
|||||||
"groups" => Value::list(groups, span),
|
"groups" => Value::list(groups, span),
|
||||||
};
|
};
|
||||||
|
|
||||||
users.push(Value::record(record, span));
|
users_list.push(Value::record(record, span));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !users.is_empty() {
|
if !users.is_empty() {
|
||||||
record.push("sessions", Value::list(users, span));
|
record.push("sessions", Value::list(users_list, span));
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::record(record, span)
|
Value::record(record, span)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn temp(span: Span) -> Value {
|
pub fn temp(span: Span) -> Value {
|
||||||
let mut sys = System::new();
|
let components = Components::new_with_refreshed_list();
|
||||||
sys.refresh_components();
|
|
||||||
sys.refresh_components_list();
|
|
||||||
|
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
|
|
||||||
for component in sys.components() {
|
for component in components.list() {
|
||||||
let mut record = record! {
|
let mut record = record! {
|
||||||
"unit" => Value::string(component.label(), span),
|
"unit" => Value::string(component.label(), span),
|
||||||
"temp" => Value::float(component.temperature() as f64, span),
|
"temp" => Value::float(component.temperature() as f64, span),
|
||||||
|
@ -1,12 +1,24 @@
|
|||||||
use nu_test_support::nu;
|
use nu_test_support::nu;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic() {
|
fn basic_stdout() {
|
||||||
let actual = nu!(r#"
|
let without_complete = nu!(r#"
|
||||||
(^echo a | complete) == {stdout: "a\n", exit_code: 0}
|
nu --testbin cococo test
|
||||||
|
"#);
|
||||||
|
let with_complete = nu!(r#"
|
||||||
|
(nu --testbin cococo test | complete).stdout
|
||||||
"#);
|
"#);
|
||||||
|
|
||||||
assert_eq!(actual.out, "true");
|
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]
|
#[test]
|
||||||
|
@ -197,7 +197,7 @@ fn def_wrapped_with_block() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn def_wrapped_from_module() {
|
fn def_wrapped_from_module() {
|
||||||
let actual = nu!(r#"module spam {
|
let actual = nu!(r#"module spam {
|
||||||
export def --wrapped my-echo [...rest] { ^echo $rest }
|
export def --wrapped my-echo [...rest] { nu --testbin cococo ...$rest }
|
||||||
}
|
}
|
||||||
|
|
||||||
use spam
|
use spam
|
||||||
|
@ -49,7 +49,7 @@ fn exec_misc_values() {
|
|||||||
let actual = nu!(
|
let actual = nu!(
|
||||||
cwd: dirs.test(), pipeline(
|
cwd: dirs.test(), pipeline(
|
||||||
r#"
|
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 ]'
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -38,3 +38,68 @@ fn http_delete_failed_due_to_server_error() {
|
|||||||
|
|
||||||
assert!(actual.err.contains("Bad request (400)"))
|
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)"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
@ -176,6 +176,71 @@ fn http_get_full_response() {
|
|||||||
assert_eq!(header["value"], "close");
|
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.
|
// 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.
|
// Revisit this if these tests prove to be flaky or unstable.
|
||||||
|
|
||||||
|
@ -39,3 +39,75 @@ fn http_head_failed_due_to_server_error() {
|
|||||||
|
|
||||||
assert!(actual.err.contains("Bad request (400)"))
|
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)"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
@ -76,3 +76,68 @@ fn http_patch_failed_due_to_unexpected_body() {
|
|||||||
|
|
||||||
assert!(actual.err.contains("Cannot make request"))
|
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)"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
@ -112,3 +112,68 @@ fn http_post_json_list_is_success() {
|
|||||||
mock.assert();
|
mock.assert();
|
||||||
assert!(actual.out.is_empty())
|
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)"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
@ -76,3 +76,68 @@ fn http_put_failed_due_to_unexpected_body() {
|
|||||||
|
|
||||||
assert!(actual.err.contains("Cannot make request"))
|
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)"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
@ -375,6 +375,19 @@ 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]
|
#[test]
|
||||||
fn removes_file_after_cd() {
|
fn removes_file_after_cd() {
|
||||||
Playground::setup("rm_after_cd", |dirs, sandbox| {
|
Playground::setup("rm_after_cd", |dirs, sandbox| {
|
||||||
|
@ -139,14 +139,13 @@ fn failed_command_with_semicolon_will_not_execute_following_cmds() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn external_args_with_quoted() {
|
fn external_args_with_quoted() {
|
||||||
Playground::setup("external failed command with semicolon", |dirs, _| {
|
Playground::setup("external failed command with semicolon", |dirs, _| {
|
||||||
let actual = nu!(
|
let actual = nu!(
|
||||||
cwd: dirs.test(), pipeline(
|
cwd: dirs.test(), pipeline(
|
||||||
r#"
|
r#"
|
||||||
^echo "foo=bar 'hi'"
|
nu --testbin cococo "foo=bar 'hi'"
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -187,14 +186,13 @@ fn external_arg_with_variable_name() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn external_command_escape_args() {
|
fn external_command_escape_args() {
|
||||||
Playground::setup("external failed command with semicolon", |dirs, _| {
|
Playground::setup("external failed command with semicolon", |dirs, _| {
|
||||||
let actual = nu!(
|
let actual = nu!(
|
||||||
cwd: dirs.test(), pipeline(
|
cwd: dirs.test(), pipeline(
|
||||||
r#"
|
r#"
|
||||||
^echo "\"abcd"
|
nu --testbin cococo "\"abcd"
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -308,13 +306,12 @@ fn can_run_batch_files_without_bat_extension() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn quotes_trimmed_when_shelling_out() {
|
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
|
// regression test for a bug where we weren't trimming quotes around string args before shelling out to cmd.exe
|
||||||
let actual = nu!(pipeline(
|
let actual = nu!(pipeline(
|
||||||
r#"
|
r#"
|
||||||
^echo "foo"
|
nu --testbin cococo "foo"
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -328,7 +325,7 @@ fn redirect_combine() {
|
|||||||
let actual = nu!(
|
let actual = nu!(
|
||||||
cwd: dirs.test(), pipeline(
|
cwd: dirs.test(), pipeline(
|
||||||
r#"
|
r#"
|
||||||
run-external --redirect-combine sh [-c 'echo Foo; echo >&2 Bar']
|
run-external --redirect-combine sh ...[-c 'echo Foo; echo >&2 Bar']
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use nu_test_support::fs::{file_contents, Stub};
|
use nu_test_support::fs::{file_contents, Stub};
|
||||||
use nu_test_support::nu;
|
|
||||||
use nu_test_support::playground::Playground;
|
use nu_test_support::playground::Playground;
|
||||||
|
use nu_test_support::{nu, pipeline};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -325,3 +325,45 @@ fn save_file_correct_relative_path() {
|
|||||||
assert_eq!(actual, "foo!");
|
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"));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -22,3 +22,39 @@ fn table_to_xml_text_and_from_xml_text_back_into_table() {
|
|||||||
|
|
||||||
assert_eq!(actual.out, "true");
|
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"));
|
||||||
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::{Call, Expression},
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
eval_const::eval_constant,
|
eval_const::eval_constant,
|
||||||
FromValue, ShellError,
|
FromValue, ShellError, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::eval_expression;
|
use crate::eval_expression;
|
||||||
@ -34,6 +34,10 @@ pub trait CallExt {
|
|||||||
starting_pos: usize,
|
starting_pos: usize,
|
||||||
) -> Result<Vec<T>, ShellError>;
|
) -> 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>(
|
fn opt<T: FromValue>(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -98,8 +102,9 @@ impl CallExt for Call {
|
|||||||
) -> Result<Vec<T>, ShellError> {
|
) -> Result<Vec<T>, ShellError> {
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
|
|
||||||
for expr in self.positional_iter().skip(starting_pos) {
|
for result in self.rest_iter_flattened(starting_pos, |expr| {
|
||||||
let result = eval_expression(engine_state, stack, expr)?;
|
eval_expression(engine_state, stack, expr)
|
||||||
|
})? {
|
||||||
output.push(FromValue::from_value(result)?);
|
output.push(FromValue::from_value(result)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,14 +118,36 @@ impl CallExt for Call {
|
|||||||
) -> Result<Vec<T>, ShellError> {
|
) -> Result<Vec<T>, ShellError> {
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
|
|
||||||
for expr in self.positional_iter().skip(starting_pos) {
|
for result in
|
||||||
let result = eval_constant(working_set, expr)?;
|
self.rest_iter_flattened(starting_pos, |expr| eval_constant(working_set, expr))?
|
||||||
|
{
|
||||||
output.push(FromValue::from_value(result)?);
|
output.push(FromValue::from_value(result)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(output)
|
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>(
|
fn opt<T: FromValue>(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use crate::{current_dir_str, get_full_help};
|
use crate::{call_ext::CallExt, current_dir_str, get_full_help};
|
||||||
use nu_path::expand_path_with;
|
use nu_path::expand_path_with;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{
|
ast::{
|
||||||
Argument, Assignment, Block, Call, Expr, Expression, PathMember, PipelineElement,
|
Argument, Assignment, Block, Call, Expr, Expression, ExternalArgument, PathMember,
|
||||||
Redirection,
|
PipelineElement, Redirection,
|
||||||
},
|
},
|
||||||
engine::{Closure, EngineState, Stack},
|
engine::{Closure, EngineState, Stack},
|
||||||
eval_base::Eval,
|
eval_base::Eval,
|
||||||
@ -66,11 +66,11 @@ pub fn eval_call(
|
|||||||
if let Some(rest_positional) = decl.signature().rest_positional {
|
if let Some(rest_positional) = decl.signature().rest_positional {
|
||||||
let mut rest_items = vec![];
|
let mut rest_items = vec![];
|
||||||
|
|
||||||
for arg in call.positional_iter().skip(
|
for result in call.rest_iter_flattened(
|
||||||
decl.signature().required_positional.len()
|
decl.signature().required_positional.len()
|
||||||
+ decl.signature().optional_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);
|
rest_items.push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,7 +182,7 @@ fn eval_external(
|
|||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
head: &Expression,
|
head: &Expression,
|
||||||
args: &[Expression],
|
args: &[ExternalArgument],
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
redirect_target: RedirectTarget,
|
redirect_target: RedirectTarget,
|
||||||
is_subexpression: bool,
|
is_subexpression: bool,
|
||||||
@ -198,7 +198,10 @@ fn eval_external(
|
|||||||
call.add_positional(head.clone());
|
call.add_positional(head.clone());
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
call.add_positional(arg.clone())
|
match arg {
|
||||||
|
ExternalArgument::Regular(expr) => call.add_positional(expr.clone()),
|
||||||
|
ExternalArgument::Spread(expr) => call.add_spread(expr.clone()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match redirect_target {
|
match redirect_target {
|
||||||
@ -947,7 +950,7 @@ impl Eval for EvalRuntime {
|
|||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
head: &Expression,
|
head: &Expression,
|
||||||
args: &[Expression],
|
args: &[ExternalArgument],
|
||||||
is_subexpression: bool,
|
is_subexpression: bool,
|
||||||
_: Span,
|
_: Span,
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<Value, ShellError> {
|
||||||
@ -1099,7 +1102,18 @@ impl Eval for EvalRuntime {
|
|||||||
.get_block(block_id)
|
.get_block(block_id)
|
||||||
.captures
|
.captures
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&id| stack.get_var(id, span).map(|var| (id, var)))
|
.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))
|
||||||
|
})
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
Ok(Value::closure(Closure { block_id, captures }, span))
|
Ok(Value::closure(Closure { block_id, captures }, span))
|
||||||
|
@ -20,7 +20,7 @@ nu-table = { path = "../nu-table", version = "0.88.2" }
|
|||||||
nu-json = { path = "../nu-json", version = "0.88.2" }
|
nu-json = { path = "../nu-json", version = "0.88.2" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.88.2" }
|
nu-utils = { path = "../nu-utils", version = "0.88.2" }
|
||||||
|
|
||||||
terminal_size = "0.2"
|
terminal_size = "0.3"
|
||||||
strip-ansi-escapes = "0.2.0"
|
strip-ansi-escapes = "0.2.0"
|
||||||
crossterm = "0.27"
|
crossterm = "0.27"
|
||||||
ratatui = "0.23"
|
ratatui = "0.23"
|
||||||
|
@ -15,7 +15,7 @@ nu-protocol = { path = "../nu-protocol", version = "0.88.2" }
|
|||||||
reedline = { version = "0.27" }
|
reedline = { version = "0.27" }
|
||||||
|
|
||||||
crossbeam-channel = "0.5.8"
|
crossbeam-channel = "0.5.8"
|
||||||
lsp-types = "0.94.1"
|
lsp-types = "0.95.0"
|
||||||
lsp-server = "0.7.5"
|
lsp-server = "0.7.5"
|
||||||
miette = "5.10"
|
miette = "5.10"
|
||||||
ropey = "1.6.1"
|
ropey = "1.6.1"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use nu_protocol::ast::{
|
use nu_protocol::ast::{
|
||||||
Block, Expr, Expression, ImportPatternMember, MatchPattern, PathMember, Pattern, Pipeline,
|
Argument, Block, Expr, Expression, ExternalArgument, ImportPatternMember, MatchPattern,
|
||||||
PipelineElement, RecordItem,
|
PathMember, Pattern, Pipeline, PipelineElement, RecordItem,
|
||||||
};
|
};
|
||||||
use nu_protocol::{engine::StateWorkingSet, Span};
|
use nu_protocol::{engine::StateWorkingSet, Span};
|
||||||
use nu_protocol::{DeclId, VarId};
|
use nu_protocol::{DeclId, VarId};
|
||||||
@ -193,17 +193,28 @@ pub fn flatten_expression(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut args = vec![];
|
let mut args = vec![];
|
||||||
for positional in call.positional_iter() {
|
for arg in &call.arguments {
|
||||||
let flattened = flatten_expression(working_set, positional);
|
match arg {
|
||||||
args.extend(flattened);
|
Argument::Positional(positional) | Argument::Unknown(positional) => {
|
||||||
}
|
let flattened = flatten_expression(working_set, positional);
|
||||||
for named in call.named_iter() {
|
args.extend(flattened);
|
||||||
if named.0.span.end != 0 {
|
}
|
||||||
// Ignore synthetic flags
|
Argument::Named(named) => {
|
||||||
args.push((named.0.span, FlatShape::Flag));
|
if named.0.span.end != 0 {
|
||||||
}
|
// Ignore synthetic flags
|
||||||
if let Some(expr) = &named.2 {
|
args.push((named.0.span, FlatShape::Flag));
|
||||||
args.extend(flatten_expression(working_set, expr));
|
}
|
||||||
|
if let Some(expr) = &named.2 {
|
||||||
|
args.extend(flatten_expression(working_set, expr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Argument::Spread(expr) => {
|
||||||
|
args.push((
|
||||||
|
Span::new(expr.span.start - 3, expr.span.start),
|
||||||
|
FlatShape::Operator,
|
||||||
|
));
|
||||||
|
args.extend(flatten_expression(working_set, expr));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// sort these since flags and positional args can be intermixed
|
// sort these since flags and positional args can be intermixed
|
||||||
@ -231,15 +242,24 @@ pub fn flatten_expression(
|
|||||||
for arg in args {
|
for arg in args {
|
||||||
//output.push((*arg, FlatShape::ExternalArg));
|
//output.push((*arg, FlatShape::ExternalArg));
|
||||||
match arg {
|
match arg {
|
||||||
Expression {
|
ExternalArgument::Regular(expr) => match expr {
|
||||||
expr: Expr::String(..),
|
Expression {
|
||||||
span,
|
expr: Expr::String(..),
|
||||||
..
|
span,
|
||||||
} => {
|
..
|
||||||
output.push((*span, FlatShape::ExternalArg));
|
} => {
|
||||||
}
|
output.push((*span, FlatShape::ExternalArg));
|
||||||
_ => {
|
}
|
||||||
output.extend(flatten_expression(working_set, arg));
|
_ => {
|
||||||
|
output.extend(flatten_expression(working_set, expr));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ExternalArgument::Spread(expr) => {
|
||||||
|
output.push((
|
||||||
|
Span::new(expr.span.start - 3, expr.span.start),
|
||||||
|
FlatShape::Operator,
|
||||||
|
));
|
||||||
|
output.extend(flatten_expression(working_set, expr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,6 +106,7 @@ impl Command for KnownExternal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Argument::Unknown(unknown) => extern_call.add_unknown(unknown.clone()),
|
Argument::Unknown(unknown) => extern_call.add_unknown(unknown.clone()),
|
||||||
|
Argument::Spread(args) => extern_call.add_spread(args.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,9 +12,9 @@ use nu_engine::DIR_VAR_PARSER_INFO;
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{
|
ast::{
|
||||||
Argument, Assignment, Bits, Block, Boolean, Call, CellPath, Comparison, Expr, Expression,
|
Argument, Assignment, Bits, Block, Boolean, Call, CellPath, Comparison, Expr, Expression,
|
||||||
FullCellPath, ImportPattern, ImportPatternHead, ImportPatternMember, MatchPattern, Math,
|
ExternalArgument, FullCellPath, ImportPattern, ImportPatternHead, ImportPatternMember,
|
||||||
Operator, PathMember, Pattern, Pipeline, PipelineElement, RangeInclusion, RangeOperator,
|
MatchPattern, Math, Operator, PathMember, Pattern, Pipeline, PipelineElement,
|
||||||
RecordItem,
|
RangeInclusion, RangeOperator, RecordItem,
|
||||||
},
|
},
|
||||||
engine::StateWorkingSet,
|
engine::StateWorkingSet,
|
||||||
eval_const::eval_constant,
|
eval_const::eval_constant,
|
||||||
@ -266,13 +266,22 @@ pub fn check_name<'a>(working_set: &mut StateWorkingSet, spans: &'a [Span]) -> O
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_external_arg(working_set: &mut StateWorkingSet, span: Span) -> Expression {
|
fn parse_external_arg(working_set: &mut StateWorkingSet, span: Span) -> ExternalArgument {
|
||||||
let contents = working_set.get_span_contents(span);
|
let contents = working_set.get_span_contents(span);
|
||||||
|
|
||||||
if contents.starts_with(b"$") || contents.starts_with(b"(") {
|
if contents.starts_with(b"$") || contents.starts_with(b"(") {
|
||||||
parse_dollar_expr(working_set, span)
|
ExternalArgument::Regular(parse_dollar_expr(working_set, span))
|
||||||
} else if contents.starts_with(b"[") {
|
} else if contents.starts_with(b"[") {
|
||||||
parse_list_expression(working_set, span, &SyntaxShape::Any)
|
ExternalArgument::Regular(parse_list_expression(working_set, span, &SyntaxShape::Any))
|
||||||
|
} else if contents.len() > 3
|
||||||
|
&& contents.starts_with(b"...")
|
||||||
|
&& (contents[3] == b'$' || contents[3] == b'[' || contents[3] == b'(')
|
||||||
|
{
|
||||||
|
ExternalArgument::Spread(parse_value(
|
||||||
|
working_set,
|
||||||
|
Span::new(span.start + 3, span.end),
|
||||||
|
&SyntaxShape::List(Box::new(SyntaxShape::Any)),
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
// Eval stage trims the quotes, so we don't have to do the same thing when parsing.
|
// Eval stage trims the quotes, so we don't have to do the same thing when parsing.
|
||||||
let contents = if contents.starts_with(b"\"") {
|
let contents = if contents.starts_with(b"\"") {
|
||||||
@ -285,12 +294,12 @@ fn parse_external_arg(working_set: &mut StateWorkingSet, span: Span) -> Expressi
|
|||||||
String::from_utf8_lossy(contents).to_string()
|
String::from_utf8_lossy(contents).to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
Expression {
|
ExternalArgument::Regular(Expression {
|
||||||
expr: Expr::String(contents),
|
expr: Expr::String(contents),
|
||||||
span,
|
span,
|
||||||
ty: Type::String,
|
ty: Type::String,
|
||||||
custom_completion: None,
|
custom_completion: None,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -978,6 +987,49 @@ pub fn parse_internal_call(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let contents = working_set.get_span_contents(spans[spans_idx]);
|
||||||
|
|
||||||
|
if contents.len() > 3
|
||||||
|
&& contents.starts_with(b"...")
|
||||||
|
&& (contents[3] == b'$' || contents[3] == b'[' || contents[3] == b'(')
|
||||||
|
{
|
||||||
|
if signature.rest_positional.is_none() && !signature.allows_unknown_args {
|
||||||
|
working_set.error(ParseError::UnexpectedSpreadArg(
|
||||||
|
signature.call_signature(),
|
||||||
|
arg_span,
|
||||||
|
));
|
||||||
|
call.add_positional(Expression::garbage(arg_span));
|
||||||
|
} else if positional_idx < signature.required_positional.len() {
|
||||||
|
working_set.error(ParseError::MissingPositional(
|
||||||
|
signature.required_positional[positional_idx].name.clone(),
|
||||||
|
Span::new(spans[spans_idx].start, spans[spans_idx].start),
|
||||||
|
signature.call_signature(),
|
||||||
|
));
|
||||||
|
call.add_positional(Expression::garbage(arg_span));
|
||||||
|
} else {
|
||||||
|
let rest_shape = match &signature.rest_positional {
|
||||||
|
Some(arg) => arg.shape.clone(),
|
||||||
|
None => SyntaxShape::Any,
|
||||||
|
};
|
||||||
|
// Parse list of arguments to be spread
|
||||||
|
let args = parse_value(
|
||||||
|
working_set,
|
||||||
|
Span::new(arg_span.start + 3, arg_span.end),
|
||||||
|
&SyntaxShape::List(Box::new(rest_shape)),
|
||||||
|
);
|
||||||
|
|
||||||
|
call.add_spread(args);
|
||||||
|
// Let the parser know that it's parsing rest arguments now
|
||||||
|
positional_idx =
|
||||||
|
signature.required_positional.len() + signature.optional_positional.len();
|
||||||
|
}
|
||||||
|
|
||||||
|
spans_idx += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Parse a positional arg if there is one
|
// Parse a positional arg if there is one
|
||||||
if let Some(positional) = signature.get_positional(positional_idx) {
|
if let Some(positional) = signature.get_positional(positional_idx) {
|
||||||
let end = calculate_end_span(working_set, &signature, spans, spans_idx, positional_idx);
|
let end = calculate_end_span(working_set, &signature, spans, spans_idx, positional_idx);
|
||||||
@ -5272,15 +5324,43 @@ pub fn parse_record(working_set: &mut StateWorkingSet, span: Span) -> Expression
|
|||||||
|
|
||||||
idx += 1;
|
idx += 1;
|
||||||
if idx == tokens.len() {
|
if idx == tokens.len() {
|
||||||
working_set.error(ParseError::Expected("record", span));
|
working_set.error(ParseError::Expected(
|
||||||
return garbage(span);
|
"':'",
|
||||||
|
Span::new(curr_span.end, curr_span.end),
|
||||||
|
));
|
||||||
|
output.push(RecordItem::Pair(
|
||||||
|
garbage(curr_span),
|
||||||
|
garbage(Span::new(curr_span.end, curr_span.end)),
|
||||||
|
));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
let colon = working_set.get_span_contents(tokens[idx].span);
|
let colon_span = tokens[idx].span;
|
||||||
|
let colon = working_set.get_span_contents(colon_span);
|
||||||
idx += 1;
|
idx += 1;
|
||||||
if idx == tokens.len() || colon != b":" {
|
if colon != b":" {
|
||||||
//FIXME: need better error
|
working_set.error(ParseError::Expected(
|
||||||
working_set.error(ParseError::Expected("record", span));
|
"':'",
|
||||||
return garbage(span);
|
Span::new(colon_span.start, colon_span.start),
|
||||||
|
));
|
||||||
|
output.push(RecordItem::Pair(
|
||||||
|
field,
|
||||||
|
garbage(Span::new(
|
||||||
|
colon_span.start,
|
||||||
|
tokens[tokens.len() - 1].span.end,
|
||||||
|
)),
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if idx == tokens.len() {
|
||||||
|
working_set.error(ParseError::Expected(
|
||||||
|
"value for record field",
|
||||||
|
Span::new(colon_span.end, colon_span.end),
|
||||||
|
));
|
||||||
|
output.push(RecordItem::Pair(
|
||||||
|
garbage(Span::new(curr_span.start, colon_span.end)),
|
||||||
|
garbage(Span::new(colon_span.end, tokens[tokens.len() - 1].span.end)),
|
||||||
|
));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
let value = parse_value(working_set, tokens[idx].span, &SyntaxShape::Any);
|
let value = parse_value(working_set, tokens[idx].span, &SyntaxShape::Any);
|
||||||
idx += 1;
|
idx += 1;
|
||||||
@ -5857,22 +5937,27 @@ pub fn discover_captures_in_expr(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for named in call.named_iter() {
|
for arg in &call.arguments {
|
||||||
if let Some(arg) = &named.2 {
|
match arg {
|
||||||
discover_captures_in_expr(working_set, arg, seen, seen_blocks, output)?;
|
Argument::Named(named) => {
|
||||||
|
if let Some(arg) = &named.2 {
|
||||||
|
discover_captures_in_expr(working_set, arg, seen, seen_blocks, output)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Argument::Positional(expr)
|
||||||
|
| Argument::Unknown(expr)
|
||||||
|
| Argument::Spread(expr) => {
|
||||||
|
discover_captures_in_expr(working_set, expr, seen, seen_blocks, output)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for positional in call.positional_iter() {
|
|
||||||
discover_captures_in_expr(working_set, positional, seen, seen_blocks, output)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Expr::CellPath(_) => {}
|
Expr::CellPath(_) => {}
|
||||||
Expr::DateTime(_) => {}
|
Expr::DateTime(_) => {}
|
||||||
Expr::ExternalCall(head, exprs, _) => {
|
Expr::ExternalCall(head, args, _) => {
|
||||||
discover_captures_in_expr(working_set, head, seen, seen_blocks, output)?;
|
discover_captures_in_expr(working_set, head, seen, seen_blocks, output)?;
|
||||||
|
|
||||||
for expr in exprs {
|
for ExternalArgument::Regular(expr) | ExternalArgument::Spread(expr) in args {
|
||||||
discover_captures_in_expr(working_set, expr, seen, seen_blocks, output)?;
|
discover_captures_in_expr(working_set, expr, seen, seen_blocks, output)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6086,11 +6171,8 @@ fn wrap_expr_with_collect(working_set: &mut StateWorkingSet, expr: &Expression)
|
|||||||
default_value: None,
|
default_value: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut expr = expr.clone();
|
|
||||||
expr.replace_in_variable(working_set, var_id);
|
|
||||||
|
|
||||||
let block = Block {
|
let block = Block {
|
||||||
pipelines: vec![Pipeline::from_vec(vec![expr])],
|
pipelines: vec![Pipeline::from_vec(vec![expr.clone()])],
|
||||||
signature: Box::new(signature),
|
signature: Box::new(signature),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use nu_engine::eval_expression;
|
use nu_engine::{eval_expression, CallExt};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{EngineState, Stack},
|
engine::{EngineState, Stack},
|
||||||
@ -33,10 +33,8 @@ impl EvaluatedCall {
|
|||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
) -> Result<Self, ShellError> {
|
) -> Result<Self, ShellError> {
|
||||||
let positional = call
|
let positional =
|
||||||
.positional_iter()
|
call.rest_iter_flattened(0, |expr| eval_expression(engine_state, stack, expr))?;
|
||||||
.map(|expr| eval_expression(engine_state, stack, expr))
|
|
||||||
.collect::<Result<Vec<Value>, ShellError>>()?;
|
|
||||||
|
|
||||||
let mut named = Vec::with_capacity(call.named_len());
|
let mut named = Vec::with_capacity(call.named_len());
|
||||||
for (string, _, expr) in call.named_iter() {
|
for (string, _, expr) in call.named_iter() {
|
||||||
|
@ -10,6 +10,7 @@ pub enum Argument {
|
|||||||
Positional(Expression),
|
Positional(Expression),
|
||||||
Named((Spanned<String>, Option<Spanned<String>>, Option<Expression>)),
|
Named((Spanned<String>, Option<Spanned<String>>, Option<Expression>)),
|
||||||
Unknown(Expression), // unknown argument used in "fall-through" signatures
|
Unknown(Expression), // unknown argument used in "fall-through" signatures
|
||||||
|
Spread(Expression), // a list spread to fill in rest arguments
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Argument {
|
impl Argument {
|
||||||
@ -30,10 +31,17 @@ impl Argument {
|
|||||||
Span::new(start, end)
|
Span::new(start, end)
|
||||||
}
|
}
|
||||||
Argument::Unknown(e) => e.span,
|
Argument::Unknown(e) => e.span,
|
||||||
|
Argument::Spread(e) => e.span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum ExternalArgument {
|
||||||
|
Regular(Expression),
|
||||||
|
Spread(Expression),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Call {
|
pub struct Call {
|
||||||
/// identifier of the declaration to call
|
/// identifier of the declaration to call
|
||||||
@ -85,6 +93,7 @@ impl Call {
|
|||||||
Argument::Named(named) => Some(named),
|
Argument::Named(named) => Some(named),
|
||||||
Argument::Positional(_) => None,
|
Argument::Positional(_) => None,
|
||||||
Argument::Unknown(_) => None,
|
Argument::Unknown(_) => None,
|
||||||
|
Argument::Spread(_) => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,6 +105,7 @@ impl Call {
|
|||||||
Argument::Named(named) => Some(named),
|
Argument::Named(named) => Some(named),
|
||||||
Argument::Positional(_) => None,
|
Argument::Positional(_) => None,
|
||||||
Argument::Unknown(_) => None,
|
Argument::Unknown(_) => None,
|
||||||
|
Argument::Spread(_) => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,26 +128,45 @@ impl Call {
|
|||||||
self.arguments.push(Argument::Unknown(unknown));
|
self.arguments.push(Argument::Unknown(unknown));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_spread(&mut self, args: Expression) {
|
||||||
|
self.arguments.push(Argument::Spread(args));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn positional_iter(&self) -> impl Iterator<Item = &Expression> {
|
pub fn positional_iter(&self) -> impl Iterator<Item = &Expression> {
|
||||||
self.arguments.iter().filter_map(|arg| match arg {
|
self.arguments
|
||||||
Argument::Named(_) => None,
|
.iter()
|
||||||
Argument::Positional(positional) => Some(positional),
|
.take_while(|arg| match arg {
|
||||||
Argument::Unknown(unknown) => Some(unknown),
|
Argument::Spread(_) => false, // Don't include positional arguments given to rest parameter
|
||||||
})
|
_ => true,
|
||||||
|
})
|
||||||
|
.filter_map(|arg| match arg {
|
||||||
|
Argument::Named(_) => None,
|
||||||
|
Argument::Positional(positional) => Some(positional),
|
||||||
|
Argument::Unknown(unknown) => Some(unknown),
|
||||||
|
Argument::Spread(_) => None,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn positional_iter_mut(&mut self) -> impl Iterator<Item = &mut Expression> {
|
pub fn positional_iter_mut(&mut self) -> impl Iterator<Item = &mut Expression> {
|
||||||
self.arguments.iter_mut().filter_map(|arg| match arg {
|
self.arguments
|
||||||
Argument::Named(_) => None,
|
.iter_mut()
|
||||||
Argument::Positional(positional) => Some(positional),
|
.take_while(|arg| match arg {
|
||||||
Argument::Unknown(unknown) => Some(unknown),
|
Argument::Spread(_) => false, // Don't include positional arguments given to rest parameter
|
||||||
})
|
_ => true,
|
||||||
|
})
|
||||||
|
.filter_map(|arg| match arg {
|
||||||
|
Argument::Named(_) => None,
|
||||||
|
Argument::Positional(positional) => Some(positional),
|
||||||
|
Argument::Unknown(unknown) => Some(unknown),
|
||||||
|
Argument::Spread(_) => None,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn positional_nth(&self, i: usize) -> Option<&Expression> {
|
pub fn positional_nth(&self, i: usize) -> Option<&Expression> {
|
||||||
self.positional_iter().nth(i)
|
self.positional_iter().nth(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO this method is never used. Delete?
|
||||||
pub fn positional_nth_mut(&mut self, i: usize) -> Option<&mut Expression> {
|
pub fn positional_nth_mut(&mut self, i: usize) -> Option<&mut Expression> {
|
||||||
self.positional_iter_mut().nth(i)
|
self.positional_iter_mut().nth(i)
|
||||||
}
|
}
|
||||||
@ -146,6 +175,24 @@ impl Call {
|
|||||||
self.positional_iter().count()
|
self.positional_iter().count()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns every argument to the rest parameter, as well as whether each argument
|
||||||
|
/// is spread or a normal positional argument (true for spread, false for normal)
|
||||||
|
pub fn rest_iter(&self, start: usize) -> impl Iterator<Item = (&Expression, bool)> {
|
||||||
|
// todo maybe rewrite to be more elegant or something
|
||||||
|
let args = self
|
||||||
|
.arguments
|
||||||
|
.iter()
|
||||||
|
.filter_map(|arg| match arg {
|
||||||
|
Argument::Named(_) => None,
|
||||||
|
Argument::Positional(positional) => Some((positional, false)),
|
||||||
|
Argument::Unknown(unknown) => Some((unknown, false)),
|
||||||
|
Argument::Spread(args) => Some((args, true)),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let spread_start = args.iter().position(|(_, spread)| *spread).unwrap_or(start);
|
||||||
|
args.into_iter().skip(start.min(spread_start))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_parser_info(&self, name: &str) -> Option<&Expression> {
|
pub fn get_parser_info(&self, name: &str) -> Option<&Expression> {
|
||||||
self.parser_info.get(name)
|
self.parser_info.get(name)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
use chrono::FixedOffset;
|
use chrono::FixedOffset;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::{Call, CellPath, Expression, FullCellPath, MatchPattern, Operator, RangeOperator};
|
use super::{
|
||||||
|
Call, CellPath, Expression, ExternalArgument, FullCellPath, MatchPattern, Operator,
|
||||||
|
RangeOperator,
|
||||||
|
};
|
||||||
use crate::{ast::ImportPattern, BlockId, Signature, Span, Spanned, Unit, VarId};
|
use crate::{ast::ImportPattern, BlockId, Signature, Span, Spanned, Unit, VarId};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
@ -19,7 +22,7 @@ pub enum Expr {
|
|||||||
Var(VarId),
|
Var(VarId),
|
||||||
VarDecl(VarId),
|
VarDecl(VarId),
|
||||||
Call(Box<Call>),
|
Call(Box<Call>),
|
||||||
ExternalCall(Box<Expression>, Vec<Expression>, bool), // head, args, is_subexpression
|
ExternalCall(Box<Expression>, Vec<ExternalArgument>, bool), // head, args, is_subexpression
|
||||||
Operator(Operator),
|
Operator(Operator),
|
||||||
RowCondition(BlockId),
|
RowCondition(BlockId),
|
||||||
UnaryNot(Box<Expression>),
|
UnaryNot(Box<Expression>),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::{Expr, RecordItem};
|
use super::{Argument, Expr, ExternalArgument, RecordItem};
|
||||||
use crate::ast::ImportPattern;
|
use crate::ast::ImportPattern;
|
||||||
use crate::DeclId;
|
use crate::DeclId;
|
||||||
use crate::{engine::StateWorkingSet, BlockId, Signature, Span, Type, VarId, IN_VARIABLE_ID};
|
use crate::{engine::StateWorkingSet, BlockId, Signature, Span, Type, VarId, IN_VARIABLE_ID};
|
||||||
@ -162,15 +162,21 @@ impl Expression {
|
|||||||
Expr::Binary(_) => false,
|
Expr::Binary(_) => false,
|
||||||
Expr::Bool(_) => false,
|
Expr::Bool(_) => false,
|
||||||
Expr::Call(call) => {
|
Expr::Call(call) => {
|
||||||
for positional in call.positional_iter() {
|
for arg in &call.arguments {
|
||||||
if positional.has_in_variable(working_set) {
|
match arg {
|
||||||
return true;
|
Argument::Positional(expr)
|
||||||
}
|
| Argument::Unknown(expr)
|
||||||
}
|
| Argument::Spread(expr) => {
|
||||||
for named in call.named_iter() {
|
if expr.has_in_variable(working_set) {
|
||||||
if let Some(expr) = &named.2 {
|
return true;
|
||||||
if expr.has_in_variable(working_set) {
|
}
|
||||||
return true;
|
}
|
||||||
|
Argument::Named(named) => {
|
||||||
|
if let Some(expr) = &named.2 {
|
||||||
|
if expr.has_in_variable(working_set) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,8 +188,8 @@ impl Expression {
|
|||||||
if head.has_in_variable(working_set) {
|
if head.has_in_variable(working_set) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for arg in args {
|
for ExternalArgument::Regular(expr) | ExternalArgument::Spread(expr) in args {
|
||||||
if arg.has_in_variable(working_set) {
|
if expr.has_in_variable(working_set) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -301,204 +307,6 @@ impl Expression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn replace_in_variable(&mut self, working_set: &mut StateWorkingSet, new_var_id: VarId) {
|
|
||||||
match &mut self.expr {
|
|
||||||
Expr::BinaryOp(left, _, right) => {
|
|
||||||
left.replace_in_variable(working_set, new_var_id);
|
|
||||||
right.replace_in_variable(working_set, new_var_id);
|
|
||||||
}
|
|
||||||
Expr::UnaryNot(expr) => {
|
|
||||||
expr.replace_in_variable(working_set, new_var_id);
|
|
||||||
}
|
|
||||||
Expr::Block(block_id) => {
|
|
||||||
let block = working_set.get_block(*block_id);
|
|
||||||
|
|
||||||
let new_expr = if let Some(pipeline) = block.pipelines.first() {
|
|
||||||
if let Some(element) = pipeline.elements.first() {
|
|
||||||
let mut new_element = element.clone();
|
|
||||||
new_element.replace_in_variable(working_set, new_var_id);
|
|
||||||
Some(new_element)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let block = working_set.get_block_mut(*block_id);
|
|
||||||
|
|
||||||
if let Some(new_expr) = new_expr {
|
|
||||||
if let Some(pipeline) = block.pipelines.get_mut(0) {
|
|
||||||
if let Some(expr) = pipeline.elements.get_mut(0) {
|
|
||||||
*expr = new_expr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
block.captures = block
|
|
||||||
.captures
|
|
||||||
.iter()
|
|
||||||
.map(|x| if *x != IN_VARIABLE_ID { *x } else { new_var_id })
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
Expr::Closure(block_id) => {
|
|
||||||
let block = working_set.get_block(*block_id);
|
|
||||||
|
|
||||||
let new_element = if let Some(pipeline) = block.pipelines.first() {
|
|
||||||
if let Some(element) = pipeline.elements.first() {
|
|
||||||
let mut new_element = element.clone();
|
|
||||||
new_element.replace_in_variable(working_set, new_var_id);
|
|
||||||
Some(new_element)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let block = working_set.get_block_mut(*block_id);
|
|
||||||
|
|
||||||
if let Some(new_element) = new_element {
|
|
||||||
if let Some(pipeline) = block.pipelines.get_mut(0) {
|
|
||||||
if let Some(element) = pipeline.elements.get_mut(0) {
|
|
||||||
*element = new_element
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
block.captures = block
|
|
||||||
.captures
|
|
||||||
.iter()
|
|
||||||
.map(|x| if *x != IN_VARIABLE_ID { *x } else { new_var_id })
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
Expr::Binary(_) => {}
|
|
||||||
Expr::Bool(_) => {}
|
|
||||||
Expr::Call(call) => {
|
|
||||||
for positional in call.positional_iter_mut() {
|
|
||||||
positional.replace_in_variable(working_set, new_var_id);
|
|
||||||
}
|
|
||||||
for named in call.named_iter_mut() {
|
|
||||||
if let Some(expr) = &mut named.2 {
|
|
||||||
expr.replace_in_variable(working_set, new_var_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expr::CellPath(_) => {}
|
|
||||||
Expr::DateTime(_) => {}
|
|
||||||
Expr::ExternalCall(head, args, _) => {
|
|
||||||
head.replace_in_variable(working_set, new_var_id);
|
|
||||||
for arg in args {
|
|
||||||
arg.replace_in_variable(working_set, new_var_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expr::Filepath(_) => {}
|
|
||||||
Expr::Directory(_) => {}
|
|
||||||
Expr::Float(_) => {}
|
|
||||||
Expr::FullCellPath(full_cell_path) => {
|
|
||||||
full_cell_path
|
|
||||||
.head
|
|
||||||
.replace_in_variable(working_set, new_var_id);
|
|
||||||
}
|
|
||||||
Expr::ImportPattern(_) => {}
|
|
||||||
Expr::Overlay(_) => {}
|
|
||||||
Expr::Garbage => {}
|
|
||||||
Expr::Nothing => {}
|
|
||||||
Expr::GlobPattern(_) => {}
|
|
||||||
Expr::Int(_) => {}
|
|
||||||
Expr::MatchBlock(_) => {}
|
|
||||||
Expr::Keyword(_, _, expr) => expr.replace_in_variable(working_set, new_var_id),
|
|
||||||
Expr::List(list) => {
|
|
||||||
for l in list {
|
|
||||||
l.replace_in_variable(working_set, new_var_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expr::Operator(_) => {}
|
|
||||||
Expr::Range(left, middle, right, ..) => {
|
|
||||||
if let Some(left) = left {
|
|
||||||
left.replace_in_variable(working_set, new_var_id)
|
|
||||||
}
|
|
||||||
if let Some(middle) = middle {
|
|
||||||
middle.replace_in_variable(working_set, new_var_id)
|
|
||||||
}
|
|
||||||
if let Some(right) = right {
|
|
||||||
right.replace_in_variable(working_set, new_var_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expr::Record(items) => {
|
|
||||||
for item in items {
|
|
||||||
match item {
|
|
||||||
RecordItem::Pair(field_name, field_value) => {
|
|
||||||
field_name.replace_in_variable(working_set, new_var_id);
|
|
||||||
field_value.replace_in_variable(working_set, new_var_id);
|
|
||||||
}
|
|
||||||
RecordItem::Spread(_, record) => {
|
|
||||||
record.replace_in_variable(working_set, new_var_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expr::Signature(_) => {}
|
|
||||||
Expr::String(_) => {}
|
|
||||||
Expr::StringInterpolation(items) => {
|
|
||||||
for i in items {
|
|
||||||
i.replace_in_variable(working_set, new_var_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => {
|
|
||||||
let block = working_set.get_block(*block_id);
|
|
||||||
|
|
||||||
let new_element = if let Some(pipeline) = block.pipelines.first() {
|
|
||||||
if let Some(element) = pipeline.elements.first() {
|
|
||||||
let mut new_element = element.clone();
|
|
||||||
new_element.replace_in_variable(working_set, new_var_id);
|
|
||||||
Some(new_element)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let block = working_set.get_block_mut(*block_id);
|
|
||||||
|
|
||||||
if let Some(new_element) = new_element {
|
|
||||||
if let Some(pipeline) = block.pipelines.get_mut(0) {
|
|
||||||
if let Some(element) = pipeline.elements.get_mut(0) {
|
|
||||||
*element = new_element
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
block.captures = block
|
|
||||||
.captures
|
|
||||||
.iter()
|
|
||||||
.map(|x| if *x != IN_VARIABLE_ID { *x } else { new_var_id })
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
Expr::Table(headers, cells) => {
|
|
||||||
for header in headers {
|
|
||||||
header.replace_in_variable(working_set, new_var_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
for row in cells {
|
|
||||||
for cell in row.iter_mut() {
|
|
||||||
cell.replace_in_variable(working_set, new_var_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::ValueWithUnit(expr, _) => expr.replace_in_variable(working_set, new_var_id),
|
|
||||||
Expr::Var(x) => {
|
|
||||||
if *x == IN_VARIABLE_ID {
|
|
||||||
*x = new_var_id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expr::VarDecl(_) => {}
|
|
||||||
Expr::Spread(expr) => expr.replace_in_variable(working_set, new_var_id),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn replace_span(
|
pub fn replace_span(
|
||||||
&mut self,
|
&mut self,
|
||||||
working_set: &mut StateWorkingSet,
|
working_set: &mut StateWorkingSet,
|
||||||
@ -544,12 +352,18 @@ impl Expression {
|
|||||||
if replaced.contains_span(call.head) {
|
if replaced.contains_span(call.head) {
|
||||||
call.head = new_span;
|
call.head = new_span;
|
||||||
}
|
}
|
||||||
for positional in call.positional_iter_mut() {
|
for arg in call.arguments.iter_mut() {
|
||||||
positional.replace_span(working_set, replaced, new_span);
|
match arg {
|
||||||
}
|
Argument::Positional(expr)
|
||||||
for named in call.named_iter_mut() {
|
| Argument::Unknown(expr)
|
||||||
if let Some(expr) = &mut named.2 {
|
| Argument::Spread(expr) => {
|
||||||
expr.replace_span(working_set, replaced, new_span)
|
expr.replace_span(working_set, replaced, new_span);
|
||||||
|
}
|
||||||
|
Argument::Named(named) => {
|
||||||
|
if let Some(expr) = &mut named.2 {
|
||||||
|
expr.replace_span(working_set, replaced, new_span);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -557,8 +371,8 @@ impl Expression {
|
|||||||
Expr::DateTime(_) => {}
|
Expr::DateTime(_) => {}
|
||||||
Expr::ExternalCall(head, args, _) => {
|
Expr::ExternalCall(head, args, _) => {
|
||||||
head.replace_span(working_set, replaced, new_span);
|
head.replace_span(working_set, replaced, new_span);
|
||||||
for arg in args {
|
for ExternalArgument::Regular(expr) | ExternalArgument::Spread(expr) in args {
|
||||||
arg.replace_span(working_set, replaced, new_span)
|
expr.replace_span(working_set, replaced, new_span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Filepath(_) => {}
|
Expr::Filepath(_) => {}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{ast::Expression, engine::StateWorkingSet, Span, VarId};
|
use crate::{ast::Expression, engine::StateWorkingSet, Span};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
|
|
||||||
@ -88,30 +88,6 @@ impl PipelineElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn replace_in_variable(&mut self, working_set: &mut StateWorkingSet, new_var_id: VarId) {
|
|
||||||
match self {
|
|
||||||
PipelineElement::Expression(_, expression)
|
|
||||||
| PipelineElement::Redirection(_, _, expression, _)
|
|
||||||
| PipelineElement::And(_, expression)
|
|
||||||
| PipelineElement::Or(_, expression)
|
|
||||||
| PipelineElement::SameTargetRedirection {
|
|
||||||
cmd: (_, expression),
|
|
||||||
..
|
|
||||||
} => expression.replace_in_variable(working_set, new_var_id),
|
|
||||||
PipelineElement::SeparateRedirection {
|
|
||||||
out: (_, out_expr, _),
|
|
||||||
err: (_, err_expr, _),
|
|
||||||
} => {
|
|
||||||
if out_expr.has_in_variable(working_set) {
|
|
||||||
out_expr.replace_in_variable(working_set, new_var_id)
|
|
||||||
}
|
|
||||||
if err_expr.has_in_variable(working_set) {
|
|
||||||
err_expr.replace_in_variable(working_set, new_var_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn replace_span(
|
pub fn replace_span(
|
||||||
&mut self,
|
&mut self,
|
||||||
working_set: &mut StateWorkingSet,
|
working_set: &mut StateWorkingSet,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
eval_operator, Assignment, Bits, Boolean, Call, Comparison, Expr, Expression, Math,
|
eval_operator, Assignment, Bits, Boolean, Call, Comparison, Expr, Expression,
|
||||||
Operator, RecordItem,
|
ExternalArgument, Math, Operator, RecordItem,
|
||||||
},
|
},
|
||||||
Range, Record, ShellError, Span, Value, VarId,
|
Range, Record, ShellError, Span, Value, VarId,
|
||||||
};
|
};
|
||||||
@ -319,7 +319,7 @@ pub trait Eval {
|
|||||||
state: Self::State<'_>,
|
state: Self::State<'_>,
|
||||||
mut_state: &mut Self::MutState,
|
mut_state: &mut Self::MutState,
|
||||||
head: &Expression,
|
head: &Expression,
|
||||||
args: &[Expression],
|
args: &[ExternalArgument],
|
||||||
is_subexpression: bool,
|
is_subexpression: bool,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Result<Value, ShellError>;
|
) -> Result<Value, ShellError>;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
ast::{Assignment, Block, Call, Expr, Expression, PipelineElement},
|
ast::{Assignment, Block, Call, Expr, Expression, ExternalArgument, PipelineElement},
|
||||||
engine::{EngineState, StateWorkingSet},
|
engine::{EngineState, StateWorkingSet},
|
||||||
eval_base::Eval,
|
eval_base::Eval,
|
||||||
record, HistoryFileFormat, PipelineData, Record, ShellError, Span, Value, VarId,
|
record, HistoryFileFormat, PipelineData, Record, ShellError, Span, Value, VarId,
|
||||||
@ -317,7 +317,7 @@ impl Eval for EvalConst {
|
|||||||
_: &StateWorkingSet,
|
_: &StateWorkingSet,
|
||||||
_: &mut (),
|
_: &mut (),
|
||||||
_: &Expression,
|
_: &Expression,
|
||||||
_: &[Expression],
|
_: &[ExternalArgument],
|
||||||
_: bool,
|
_: bool,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<Value, ShellError> {
|
||||||
|
@ -484,9 +484,12 @@ pub enum ParseError {
|
|||||||
#[label("...and here")] Option<Span>,
|
#[label("...and here")] Option<Span>,
|
||||||
),
|
),
|
||||||
|
|
||||||
#[error("Unexpected spread operator outside list")]
|
#[error("This command does not have a ...rest parameter")]
|
||||||
#[diagnostic(code(nu::parser::unexpected_spread_operator))]
|
#[diagnostic(
|
||||||
UnexpectedSpread(#[label("Spread operator not allowed here")] Span),
|
code(nu::parser::unexpected_spread_arg),
|
||||||
|
help("To spread arguments, the command needs to define a multi-positional parameter in its signature, such as ...rest")
|
||||||
|
)]
|
||||||
|
UnexpectedSpreadArg(String, #[label = "unexpected spread argument"] Span),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParseError {
|
impl ParseError {
|
||||||
@ -573,7 +576,7 @@ impl ParseError {
|
|||||||
ParseError::InvalidLiteral(_, _, s) => *s,
|
ParseError::InvalidLiteral(_, _, s) => *s,
|
||||||
ParseError::LabeledErrorWithHelp { span: s, .. } => *s,
|
ParseError::LabeledErrorWithHelp { span: s, .. } => *s,
|
||||||
ParseError::RedirectionInLetMut(s, _) => *s,
|
ParseError::RedirectionInLetMut(s, _) => *s,
|
||||||
ParseError::UnexpectedSpread(s) => *s,
|
ParseError::UnexpectedSpreadArg(_, s) => *s,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ use crate::{
|
|||||||
format_error, Config, ListStream, RawStream, ShellError, Span, Value,
|
format_error, Config, ListStream, RawStream, ShellError, Span, Value,
|
||||||
};
|
};
|
||||||
use nu_utils::{stderr_write_all_and_flush, stdout_write_all_and_flush};
|
use nu_utils::{stderr_write_all_and_flush, stdout_write_all_and_flush};
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::sync::{atomic::AtomicBool, Arc};
|
use std::sync::{atomic::AtomicBool, Arc};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
@ -62,6 +63,7 @@ pub struct PipelineMetadata {
|
|||||||
pub enum DataSource {
|
pub enum DataSource {
|
||||||
Ls,
|
Ls,
|
||||||
HtmlThemes,
|
HtmlThemes,
|
||||||
|
FilePath(PathBuf),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PipelineData {
|
impl PipelineData {
|
||||||
@ -859,10 +861,13 @@ pub fn print_if_stream(
|
|||||||
// NOTE: currently we don't need anything from stderr
|
// NOTE: currently we don't need anything from stderr
|
||||||
// so we just consume and throw away `stderr_stream` to make sure the pipe doesn't fill up
|
// so we just consume and throw away `stderr_stream` to make sure the pipe doesn't fill up
|
||||||
|
|
||||||
thread::Builder::new()
|
if let Some(stderr_stream) = stderr_stream {
|
||||||
.name("stderr consumer".to_string())
|
thread::Builder::new()
|
||||||
.spawn(move || stderr_stream.map(|x| x.into_bytes()))
|
.name("stderr consumer".to_string())
|
||||||
.expect("could not create thread");
|
.spawn(move || stderr_stream.into_bytes())
|
||||||
|
.expect("could not create thread");
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(stream) = stream {
|
if let Some(stream) = stream {
|
||||||
for s in stream {
|
for s in stream {
|
||||||
let s_live = s?;
|
let s_live = s?;
|
||||||
|
@ -1281,15 +1281,15 @@ This is an internal Nushell error, please file an issue https://github.com/nushe
|
|||||||
span: Span,
|
span: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Tried spreading a non-list inside a list.
|
/// Tried spreading a non-list inside a list or command call.
|
||||||
///
|
///
|
||||||
/// ## Resolution
|
/// ## Resolution
|
||||||
///
|
///
|
||||||
/// Only lists can be spread inside lists. Try converting the value to a list before spreading.
|
/// Only lists can be spread inside lists and command calls. Try converting the value to a list before spreading.
|
||||||
#[error("Not a list")]
|
#[error("Not a list")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code(nu::shell::cannot_spread_as_list),
|
code(nu::shell::cannot_spread_as_list),
|
||||||
help("Only lists can be spread inside lists. Try converting the value to a list before spreading")
|
help("Only lists can be spread inside lists and command calls. Try converting the value to a list before spreading.")
|
||||||
)]
|
)]
|
||||||
CannotSpreadAsList {
|
CannotSpreadAsList {
|
||||||
#[label = "cannot spread value"]
|
#[label = "cannot spread value"]
|
||||||
|
@ -90,127 +90,6 @@ export def --env "path add" [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# print a command name as dimmed and italic
|
|
||||||
def pretty-command [] {
|
|
||||||
let command = $in
|
|
||||||
return $"(ansi default_dimmed)(ansi default_italic)($command)(ansi reset)"
|
|
||||||
}
|
|
||||||
|
|
||||||
# give a hint error when the clip command is not available on the system
|
|
||||||
def check-clipboard [
|
|
||||||
clipboard: string # the clipboard command name
|
|
||||||
--system: string # some information about the system running, for better error
|
|
||||||
] {
|
|
||||||
if (which $clipboard | is-empty) {
|
|
||||||
error make --unspanned {
|
|
||||||
msg: $"(ansi red)clipboard_not_found(ansi reset):
|
|
||||||
you are running ($system)
|
|
||||||
but
|
|
||||||
the ($clipboard | pretty-command) clipboard command was not found on your system."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# put the end of a pipe into the system clipboard.
|
|
||||||
#
|
|
||||||
# Dependencies:
|
|
||||||
# - xclip on linux x11
|
|
||||||
# - wl-copy on linux wayland
|
|
||||||
# - clip.exe on windows
|
|
||||||
# - termux-api on termux
|
|
||||||
#
|
|
||||||
# Examples:
|
|
||||||
# put a simple string to the clipboard, will be stripped to remove ANSI sequences
|
|
||||||
# >_ "my wonderful string" | clip
|
|
||||||
# my wonderful string
|
|
||||||
# saved to clipboard (stripped)
|
|
||||||
#
|
|
||||||
# put a whole table to the clipboard
|
|
||||||
# >_ ls *.toml | clip
|
|
||||||
# ╭───┬─────────────────────┬──────┬────────┬───────────────╮
|
|
||||||
# │ # │ name │ type │ size │ modified │
|
|
||||||
# ├───┼─────────────────────┼──────┼────────┼───────────────┤
|
|
||||||
# │ 0 │ Cargo.toml │ file │ 5.0 KB │ 3 minutes ago │
|
|
||||||
# │ 1 │ Cross.toml │ file │ 363 B │ 2 weeks ago │
|
|
||||||
# │ 2 │ rust-toolchain.toml │ file │ 1.1 KB │ 2 weeks ago │
|
|
||||||
# ╰───┴─────────────────────┴──────┴────────┴───────────────╯
|
|
||||||
#
|
|
||||||
# saved to clipboard
|
|
||||||
#
|
|
||||||
# put huge structured data in the clipboard, but silently
|
|
||||||
# >_ open Cargo.toml --raw | from toml | clip --silent
|
|
||||||
#
|
|
||||||
# when the clipboard system command is not installed
|
|
||||||
# >_ "mm this is fishy..." | clip
|
|
||||||
# Error:
|
|
||||||
# × clipboard_not_found:
|
|
||||||
# │ you are using xorg on linux
|
|
||||||
# │ but
|
|
||||||
# │ the xclip clipboard command was not found on your system.
|
|
||||||
export def clip [
|
|
||||||
--silent # do not print the content of the clipboard to the standard output
|
|
||||||
--no-notify # do not throw a notification (only on linux)
|
|
||||||
--no-strip # do not strip ANSI escape sequences from a string
|
|
||||||
--expand (-e) # auto-expand the data given as input
|
|
||||||
--codepage (-c): int # the id of the codepage to use (only on Windows), see https://en.wikipedia.org/wiki/Windows_code_page, e.g. 65001 is for UTF-8
|
|
||||||
] {
|
|
||||||
let input = $in
|
|
||||||
|
|
||||||
print $"Warning: (char -u 26a0) (ansi yellow_bold)deprecated_command(ansi reset)"
|
|
||||||
print "| the `std clip` command is deprecated and will be removed in Nushell 0.89"
|
|
||||||
print ""
|
|
||||||
print $"(ansi cyan)help(ansi reset): please use (ansi {fg: cyan, attr: du})[`modules/system clip`]\(https://github.com/nushell/nu_scripts/tree/main/modules#system\)(ansi reset)"
|
|
||||||
|
|
||||||
let input = $input
|
|
||||||
| if $expand { table --expand } else { table }
|
|
||||||
| into string
|
|
||||||
| if $no_strip {} else { ansi strip }
|
|
||||||
|
|
||||||
match $nu.os-info.name {
|
|
||||||
"linux" => {
|
|
||||||
if ($env.WAYLAND_DISPLAY? | is-empty) {
|
|
||||||
check-clipboard xclip --system $"('xorg' | pretty-command) on linux"
|
|
||||||
$input | xclip -sel clip
|
|
||||||
} else {
|
|
||||||
check-clipboard wl-copy --system $"('wayland' | pretty-command) on linux"
|
|
||||||
$input | wl-copy
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"windows" => {
|
|
||||||
if $codepage != null {
|
|
||||||
chcp $codepage
|
|
||||||
}
|
|
||||||
check-clipboard clip.exe --system "Windows"
|
|
||||||
$input | clip.exe
|
|
||||||
},
|
|
||||||
"macos" => {
|
|
||||||
check-clipboard pbcopy --system "MacOS"
|
|
||||||
$input | pbcopy
|
|
||||||
},
|
|
||||||
"android" => {
|
|
||||||
check-clipboard termux-clipboard-set --system "Termux"
|
|
||||||
$input | termux-clipboard-set
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
error make --unspanned {
|
|
||||||
msg: $"(ansi red)unknown_operating_system(ansi reset):
|
|
||||||
'($nu.os-info.name)' is not supported by the ('clip' | pretty-command) command.
|
|
||||||
|
|
||||||
please open a feature request in the [issue tracker](char lparen)https://github.com/nushell/nushell/issues/new/choose(char rparen) to add your operating system to the standard library."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if not $silent {
|
|
||||||
print $input
|
|
||||||
print $"(ansi white_italic)(ansi white_dimmed)saved to clipboard(ansi reset)"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (not $no_notify) and ($nu.os-info.name == linux) {
|
|
||||||
notify-send "std clip" "saved to clipboard"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# convert an integer amount of nanoseconds to a real duration
|
# convert an integer amount of nanoseconds to a real duration
|
||||||
def "from ns" [] {
|
def "from ns" [] {
|
||||||
[$in "ns"] | str join | into duration
|
[$in "ns"] | str join | into duration
|
||||||
|
@ -15,7 +15,7 @@ bench = false
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
sysinfo = "0.29"
|
sysinfo = "0.30"
|
||||||
|
|
||||||
[target.'cfg(target_family = "unix")'.dependencies]
|
[target.'cfg(target_family = "unix")'.dependencies]
|
||||||
nix = { version = "0.27", default-features = false, features = ["fs", "term", "process", "signal"] }
|
nix = { version = "0.27", default-features = false, features = ["fs", "term", "process", "signal"] }
|
||||||
@ -28,36 +28,20 @@ libproc = "0.14"
|
|||||||
mach2 = "0.4"
|
mach2 = "0.4"
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
chrono = { version = "0.4", default-features = false }
|
chrono = { version = "0.4", default-features = false, features = ["clock"] }
|
||||||
ntapi = "0.4"
|
ntapi = "0.4"
|
||||||
once_cell = "1.18"
|
once_cell = "1.18"
|
||||||
winapi = { version = "0.3", features = [
|
windows = { version = "0.52", features = [
|
||||||
"tlhelp32",
|
"Wdk_System_SystemServices",
|
||||||
"fileapi",
|
"Wdk_System_Threading",
|
||||||
"handleapi",
|
"Win32_Foundation",
|
||||||
"ifdef",
|
"Win32_Security",
|
||||||
"ioapiset",
|
"Win32_System_Diagnostics_Debug",
|
||||||
"minwindef",
|
"Win32_System_Diagnostics_ToolHelp",
|
||||||
"pdh",
|
"Win32_System_Kernel",
|
||||||
"psapi",
|
"Win32_System_Memory",
|
||||||
"synchapi",
|
"Win32_System_ProcessStatus",
|
||||||
"sysinfoapi",
|
"Win32_System_SystemInformation",
|
||||||
"winbase",
|
"Win32_System_Threading",
|
||||||
"winerror",
|
"Win32_UI_Shell",
|
||||||
"winioctl",
|
]}
|
||||||
"winnt",
|
|
||||||
"oleauto",
|
|
||||||
"wbemcli",
|
|
||||||
"rpcdce",
|
|
||||||
"combaseapi",
|
|
||||||
"objidl",
|
|
||||||
"powerbase",
|
|
||||||
"netioapi",
|
|
||||||
"lmcons",
|
|
||||||
"lmaccess",
|
|
||||||
"lmapibuf",
|
|
||||||
"memoryapi",
|
|
||||||
"shellapi",
|
|
||||||
"std",
|
|
||||||
"securitybaseapi",
|
|
||||||
] }
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
use sysinfo::SystemExt;
|
|
||||||
|
|
||||||
pub fn get_os_name() -> &'static str {
|
pub fn get_os_name() -> &'static str {
|
||||||
std::env::consts::OS
|
std::env::consts::OS
|
||||||
}
|
}
|
||||||
@ -13,8 +11,7 @@ pub fn get_os_family() -> &'static str {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_kernel_version() -> String {
|
pub fn get_kernel_version() -> String {
|
||||||
let sys = sysinfo::System::new();
|
match sysinfo::System::kernel_version() {
|
||||||
match sys.kernel_version() {
|
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => "unknown".to_string(),
|
None => "unknown".to_string(),
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,10 @@
|
|||||||
use chrono::offset::TimeZone;
|
use chrono::offset::TimeZone;
|
||||||
use chrono::{Local, NaiveDate};
|
use chrono::{Local, NaiveDate};
|
||||||
use libc::c_void;
|
use libc::c_void;
|
||||||
use ntapi::ntpebteb::PEB;
|
|
||||||
use ntapi::ntpsapi::{
|
use ntapi::ntrtl::RTL_USER_PROCESS_PARAMETERS;
|
||||||
NtQueryInformationProcess, ProcessBasicInformation, ProcessCommandLineInformation,
|
use ntapi::ntwow64::{PEB32, RTL_USER_PROCESS_PARAMETERS32};
|
||||||
ProcessWow64Information, PROCESSINFOCLASS, PROCESS_BASIC_INFORMATION,
|
|
||||||
};
|
|
||||||
use ntapi::ntrtl::{RtlGetVersion, PRTL_USER_PROCESS_PARAMETERS, RTL_USER_PROCESS_PARAMETERS};
|
|
||||||
use ntapi::ntwow64::{PEB32, PRTL_USER_PROCESS_PARAMETERS32, RTL_USER_PROCESS_PARAMETERS32};
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -22,33 +19,48 @@ use std::ptr;
|
|||||||
use std::ptr::null_mut;
|
use std::ptr::null_mut;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use winapi::shared::basetsd::SIZE_T;
|
|
||||||
use winapi::shared::minwindef::{DWORD, FALSE, FILETIME, LPVOID, MAX_PATH, TRUE, ULONG};
|
use windows::core::{PCWSTR, PWSTR};
|
||||||
use winapi::shared::ntdef::{NT_SUCCESS, UNICODE_STRING};
|
|
||||||
use winapi::shared::ntstatus::{
|
use windows::Wdk::System::SystemServices::RtlGetVersion;
|
||||||
STATUS_BUFFER_OVERFLOW, STATUS_BUFFER_TOO_SMALL, STATUS_INFO_LENGTH_MISMATCH,
|
use windows::Wdk::System::Threading::{
|
||||||
|
NtQueryInformationProcess, ProcessBasicInformation, ProcessCommandLineInformation,
|
||||||
|
ProcessWow64Information, PROCESSINFOCLASS,
|
||||||
};
|
};
|
||||||
use winapi::um::handleapi::CloseHandle;
|
|
||||||
use winapi::um::memoryapi::{ReadProcessMemory, VirtualQueryEx};
|
use windows::Win32::Foundation::{
|
||||||
use winapi::um::processthreadsapi::{
|
CloseHandle, LocalFree, FALSE, FILETIME, HANDLE, HLOCAL, HMODULE, MAX_PATH, PSID,
|
||||||
GetCurrentProcess, GetPriorityClass, GetProcessTimes, OpenProcess, OpenProcessToken,
|
STATUS_BUFFER_OVERFLOW, STATUS_BUFFER_TOO_SMALL, STATUS_INFO_LENGTH_MISMATCH, UNICODE_STRING,
|
||||||
};
|
};
|
||||||
use winapi::um::psapi::{
|
|
||||||
|
use windows::Win32::Security::{
|
||||||
|
AdjustTokenPrivileges, GetTokenInformation, LookupAccountSidW, LookupPrivilegeValueW,
|
||||||
|
TokenGroups, TokenUser, SE_DEBUG_NAME, SE_PRIVILEGE_ENABLED, SID, SID_NAME_USE,
|
||||||
|
TOKEN_ADJUST_PRIVILEGES, TOKEN_GROUPS, TOKEN_PRIVILEGES, TOKEN_QUERY, TOKEN_USER,
|
||||||
|
};
|
||||||
|
|
||||||
|
use windows::Win32::System::Diagnostics::Debug::ReadProcessMemory;
|
||||||
|
use windows::Win32::System::Diagnostics::ToolHelp::{
|
||||||
|
CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS,
|
||||||
|
};
|
||||||
|
|
||||||
|
use windows::Win32::System::Memory::{VirtualQueryEx, MEMORY_BASIC_INFORMATION};
|
||||||
|
|
||||||
|
use windows::Win32::System::ProcessStatus::{
|
||||||
GetModuleBaseNameW, GetProcessMemoryInfo, K32EnumProcesses, PROCESS_MEMORY_COUNTERS,
|
GetModuleBaseNameW, GetProcessMemoryInfo, K32EnumProcesses, PROCESS_MEMORY_COUNTERS,
|
||||||
PROCESS_MEMORY_COUNTERS_EX,
|
PROCESS_MEMORY_COUNTERS_EX,
|
||||||
};
|
};
|
||||||
use winapi::um::securitybaseapi::{AdjustTokenPrivileges, GetTokenInformation};
|
|
||||||
use winapi::um::tlhelp32::{
|
use windows::Win32::System::SystemInformation::OSVERSIONINFOEXW;
|
||||||
CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS,
|
|
||||||
};
|
use windows::Win32::System::Threading::{
|
||||||
use winapi::um::winbase::{GetProcessIoCounters, LookupAccountSidW, LookupPrivilegeValueW};
|
GetCurrentProcess, GetPriorityClass, GetProcessIoCounters, GetProcessTimes, OpenProcess,
|
||||||
use winapi::um::winnt::{
|
OpenProcessToken, IO_COUNTERS, PEB, PROCESS_BASIC_INFORMATION, PROCESS_QUERY_INFORMATION,
|
||||||
TokenGroups, TokenUser, HANDLE, IO_COUNTERS, MEMORY_BASIC_INFORMATION,
|
PROCESS_VM_READ,
|
||||||
PROCESS_QUERY_INFORMATION, PROCESS_VM_READ, PSID, RTL_OSVERSIONINFOEXW, SE_DEBUG_NAME,
|
|
||||||
SE_PRIVILEGE_ENABLED, SID, TOKEN_ADJUST_PRIVILEGES, TOKEN_GROUPS, TOKEN_PRIVILEGES,
|
|
||||||
TOKEN_QUERY, TOKEN_USER,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use windows::Win32::UI::Shell::CommandLineToArgvW;
|
||||||
|
|
||||||
pub struct ProcessInfo {
|
pub struct ProcessInfo {
|
||||||
pub pid: i32,
|
pub pid: i32,
|
||||||
pub command: String,
|
pub command: String,
|
||||||
@ -235,7 +247,7 @@ pub fn collect_proc(interval: Duration, _with_thread: bool) -> Vec<ProcessInfo>
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
CloseHandle(handle);
|
let _ = CloseHandle(handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -249,32 +261,20 @@ fn set_privilege() -> bool {
|
|||||||
let handle = GetCurrentProcess();
|
let handle = GetCurrentProcess();
|
||||||
let mut token: HANDLE = zeroed();
|
let mut token: HANDLE = zeroed();
|
||||||
let ret = OpenProcessToken(handle, TOKEN_ADJUST_PRIVILEGES, &mut token);
|
let ret = OpenProcessToken(handle, TOKEN_ADJUST_PRIVILEGES, &mut token);
|
||||||
if ret == 0 {
|
if ret.is_err() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut tps: TOKEN_PRIVILEGES = zeroed();
|
let mut tps: TOKEN_PRIVILEGES = zeroed();
|
||||||
let se_debug_name: Vec<u16> = format!("{}\0", SE_DEBUG_NAME).encode_utf16().collect();
|
|
||||||
tps.PrivilegeCount = 1;
|
tps.PrivilegeCount = 1;
|
||||||
let ret = LookupPrivilegeValueW(
|
if LookupPrivilegeValueW(PCWSTR::null(), SE_DEBUG_NAME, &mut tps.Privileges[0].Luid)
|
||||||
ptr::null(),
|
.is_err()
|
||||||
se_debug_name.as_ptr(),
|
{
|
||||||
&mut tps.Privileges[0].Luid,
|
|
||||||
);
|
|
||||||
if ret == 0 {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
tps.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
tps.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||||
let ret = AdjustTokenPrivileges(
|
if AdjustTokenPrivileges(token, FALSE, Some(&tps), 0, None, None).is_err() {
|
||||||
token,
|
|
||||||
FALSE,
|
|
||||||
&mut tps,
|
|
||||||
0,
|
|
||||||
ptr::null::<TOKEN_PRIVILEGES>() as *mut TOKEN_PRIVILEGES,
|
|
||||||
ptr::null::<u32>() as *mut u32,
|
|
||||||
);
|
|
||||||
if ret == 0 {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,21 +284,21 @@ fn set_privilege() -> bool {
|
|||||||
|
|
||||||
#[cfg_attr(tarpaulin, skip)]
|
#[cfg_attr(tarpaulin, skip)]
|
||||||
fn get_pids() -> Vec<i32> {
|
fn get_pids() -> Vec<i32> {
|
||||||
let dword_size = size_of::<DWORD>();
|
let dword_size = size_of::<u32>();
|
||||||
let mut pids: Vec<DWORD> = Vec::with_capacity(10192);
|
let mut pids: Vec<u32> = Vec::with_capacity(10192);
|
||||||
let mut cb_needed = 0;
|
let mut cb_needed = 0;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
pids.set_len(10192);
|
pids.set_len(10192);
|
||||||
let result = K32EnumProcesses(
|
let result = K32EnumProcesses(
|
||||||
pids.as_mut_ptr(),
|
pids.as_mut_ptr(),
|
||||||
(dword_size * pids.len()) as DWORD,
|
(dword_size * pids.len()) as u32,
|
||||||
&mut cb_needed,
|
&mut cb_needed,
|
||||||
);
|
);
|
||||||
if result == 0 {
|
if !result.as_bool() {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
let pids_len = cb_needed / dword_size as DWORD;
|
let pids_len = cb_needed / dword_size as u32;
|
||||||
pids.set_len(pids_len as usize);
|
pids.set_len(pids_len as usize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,18 +311,20 @@ fn get_ppid_threads() -> (HashMap<i32, i32>, HashMap<i32, i32>) {
|
|||||||
let mut threads = HashMap::new();
|
let mut threads = HashMap::new();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
let Ok(snapshot) = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) else {
|
||||||
|
return (ppids, threads);
|
||||||
|
};
|
||||||
let mut entry: PROCESSENTRY32 = zeroed();
|
let mut entry: PROCESSENTRY32 = zeroed();
|
||||||
entry.dwSize = size_of::<PROCESSENTRY32>() as u32;
|
entry.dwSize = size_of::<PROCESSENTRY32>() as u32;
|
||||||
let mut not_the_end = Process32First(snapshot, &mut entry);
|
let mut not_the_end = Process32First(snapshot, &mut entry);
|
||||||
|
|
||||||
while not_the_end != 0 {
|
while not_the_end.is_ok() {
|
||||||
ppids.insert(entry.th32ProcessID as i32, entry.th32ParentProcessID as i32);
|
ppids.insert(entry.th32ProcessID as i32, entry.th32ParentProcessID as i32);
|
||||||
threads.insert(entry.th32ProcessID as i32, entry.cntThreads as i32);
|
threads.insert(entry.th32ProcessID as i32, entry.cntThreads as i32);
|
||||||
not_the_end = Process32Next(snapshot, &mut entry);
|
not_the_end = Process32Next(snapshot, &mut entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
CloseHandle(snapshot);
|
let _ = CloseHandle(snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
(ppids, threads)
|
(ppids, threads)
|
||||||
@ -338,14 +340,14 @@ fn get_handle(pid: i32) -> Option<HANDLE> {
|
|||||||
OpenProcess(
|
OpenProcess(
|
||||||
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
|
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
|
||||||
FALSE,
|
FALSE,
|
||||||
pid as DWORD,
|
pid as u32,
|
||||||
)
|
)
|
||||||
};
|
}
|
||||||
|
.ok();
|
||||||
|
|
||||||
if handle.is_null() {
|
match handle {
|
||||||
None
|
Some(h) if h.is_invalid() => None,
|
||||||
} else {
|
h => h,
|
||||||
Some(handle)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,7 +372,7 @@ fn get_times(handle: HANDLE) -> Option<(u64, u64, u64, u64)> {
|
|||||||
let sys = u64::from(sys.dwHighDateTime) << 32 | u64::from(sys.dwLowDateTime);
|
let sys = u64::from(sys.dwHighDateTime) << 32 | u64::from(sys.dwLowDateTime);
|
||||||
let user = u64::from(user.dwHighDateTime) << 32 | u64::from(user.dwLowDateTime);
|
let user = u64::from(user.dwHighDateTime) << 32 | u64::from(user.dwLowDateTime);
|
||||||
|
|
||||||
if ret != 0 {
|
if ret.is_ok() {
|
||||||
Some((start, exit, sys, user))
|
Some((start, exit, sys, user))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -386,10 +388,10 @@ fn get_memory_info(handle: HANDLE) -> Option<MemoryInfo> {
|
|||||||
handle,
|
handle,
|
||||||
&mut pmc as *mut PROCESS_MEMORY_COUNTERS_EX as *mut c_void
|
&mut pmc as *mut PROCESS_MEMORY_COUNTERS_EX as *mut c_void
|
||||||
as *mut PROCESS_MEMORY_COUNTERS,
|
as *mut PROCESS_MEMORY_COUNTERS,
|
||||||
size_of::<PROCESS_MEMORY_COUNTERS_EX>() as DWORD,
|
size_of::<PROCESS_MEMORY_COUNTERS_EX>() as u32,
|
||||||
);
|
);
|
||||||
|
|
||||||
if ret != 0 {
|
if ret.is_ok() {
|
||||||
let info = MemoryInfo {
|
let info = MemoryInfo {
|
||||||
page_fault_count: u64::from(pmc.PageFaultCount),
|
page_fault_count: u64::from(pmc.PageFaultCount),
|
||||||
peak_working_set_size: pmc.PeakWorkingSetSize as u64,
|
peak_working_set_size: pmc.PeakWorkingSetSize as u64,
|
||||||
@ -412,15 +414,10 @@ fn get_memory_info(handle: HANDLE) -> Option<MemoryInfo> {
|
|||||||
#[cfg_attr(tarpaulin, skip)]
|
#[cfg_attr(tarpaulin, skip)]
|
||||||
fn get_command(handle: HANDLE) -> Option<String> {
|
fn get_command(handle: HANDLE) -> Option<String> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut exe_buf = [0u16; MAX_PATH + 1];
|
let mut exe_buf = [0u16; MAX_PATH as usize + 1];
|
||||||
let h_mod = std::ptr::null_mut();
|
let h_mod = HMODULE::default();
|
||||||
|
|
||||||
let ret = GetModuleBaseNameW(
|
let ret = GetModuleBaseNameW(handle, h_mod, exe_buf.as_mut_slice());
|
||||||
handle,
|
|
||||||
h_mod as _,
|
|
||||||
exe_buf.as_mut_ptr(),
|
|
||||||
MAX_PATH as DWORD + 1,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut pos = 0;
|
let mut pos = 0;
|
||||||
for x in exe_buf.iter() {
|
for x in exe_buf.iter() {
|
||||||
@ -460,7 +457,7 @@ macro_rules! impl_RtlUserProcessParameters {
|
|||||||
fn get_environ(&self, handle: HANDLE) -> Result<Vec<u16>, &'static str> {
|
fn get_environ(&self, handle: HANDLE) -> Result<Vec<u16>, &'static str> {
|
||||||
let ptr = self.Environment;
|
let ptr = self.Environment;
|
||||||
unsafe {
|
unsafe {
|
||||||
let size = get_region_size(handle, ptr as LPVOID)?;
|
let size = get_region_size(handle, ptr as _)?;
|
||||||
get_process_data(handle, ptr as _, size as _)
|
get_process_data(handle, ptr as _, size as _)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -483,30 +480,41 @@ unsafe fn null_terminated_wchar_to_string(slice: &[u16]) -> String {
|
|||||||
#[allow(clippy::uninit_vec)]
|
#[allow(clippy::uninit_vec)]
|
||||||
unsafe fn get_process_data(
|
unsafe fn get_process_data(
|
||||||
handle: HANDLE,
|
handle: HANDLE,
|
||||||
ptr: LPVOID,
|
ptr: *const c_void,
|
||||||
size: usize,
|
size: usize,
|
||||||
) -> Result<Vec<u16>, &'static str> {
|
) -> Result<Vec<u16>, &'static str> {
|
||||||
let mut buffer: Vec<u16> = Vec::with_capacity(size / 2 + 1);
|
let mut buffer: Vec<u16> = Vec::with_capacity(size / 2 + 1);
|
||||||
buffer.set_len(size / 2);
|
let mut bytes_read = 0;
|
||||||
|
|
||||||
if ReadProcessMemory(
|
if ReadProcessMemory(
|
||||||
handle,
|
handle,
|
||||||
ptr as *mut _,
|
ptr,
|
||||||
buffer.as_mut_ptr() as *mut _,
|
buffer.as_mut_ptr().cast(),
|
||||||
size,
|
size,
|
||||||
std::ptr::null_mut(),
|
Some(&mut bytes_read),
|
||||||
) != TRUE
|
)
|
||||||
|
.is_err()
|
||||||
{
|
{
|
||||||
return Err("Unable to read process data");
|
return Err("Unable to read process data");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Documentation states that the function fails if not all data is accessible.
|
||||||
|
if bytes_read != size {
|
||||||
|
return Err("ReadProcessMemory returned unexpected number of bytes read");
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.set_len(size / 2);
|
||||||
|
buffer.push(0);
|
||||||
|
|
||||||
Ok(buffer)
|
Ok(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn get_region_size(handle: HANDLE, ptr: LPVOID) -> Result<usize, &'static str> {
|
unsafe fn get_region_size(handle: HANDLE, ptr: *const c_void) -> Result<usize, &'static str> {
|
||||||
let mut meminfo = MaybeUninit::<MEMORY_BASIC_INFORMATION>::uninit();
|
let mut meminfo = MaybeUninit::<MEMORY_BASIC_INFORMATION>::uninit();
|
||||||
if VirtualQueryEx(
|
if VirtualQueryEx(
|
||||||
handle,
|
handle,
|
||||||
ptr,
|
Some(ptr),
|
||||||
meminfo.as_mut_ptr() as *mut _,
|
meminfo.as_mut_ptr().cast(),
|
||||||
size_of::<MEMORY_BASIC_INFORMATION>(),
|
size_of::<MEMORY_BASIC_INFORMATION>(),
|
||||||
) == 0
|
) == 0
|
||||||
{
|
{
|
||||||
@ -521,46 +529,51 @@ unsafe fn ph_query_process_variable_size(
|
|||||||
process_handle: HANDLE,
|
process_handle: HANDLE,
|
||||||
process_information_class: PROCESSINFOCLASS,
|
process_information_class: PROCESSINFOCLASS,
|
||||||
) -> Option<Vec<u16>> {
|
) -> Option<Vec<u16>> {
|
||||||
let mut return_length = MaybeUninit::<ULONG>::uninit();
|
let mut return_length = MaybeUninit::<u32>::uninit();
|
||||||
|
|
||||||
let mut status = NtQueryInformationProcess(
|
if let Err(err) = NtQueryInformationProcess(
|
||||||
process_handle,
|
process_handle,
|
||||||
process_information_class,
|
process_information_class,
|
||||||
std::ptr::null_mut(),
|
std::ptr::null_mut(),
|
||||||
0,
|
0,
|
||||||
return_length.as_mut_ptr() as *mut _,
|
return_length.as_mut_ptr() as *mut _,
|
||||||
);
|
)
|
||||||
|
.ok()
|
||||||
if status != STATUS_BUFFER_OVERFLOW
|
|
||||||
&& status != STATUS_BUFFER_TOO_SMALL
|
|
||||||
&& status != STATUS_INFO_LENGTH_MISMATCH
|
|
||||||
{
|
{
|
||||||
return None;
|
if ![
|
||||||
|
STATUS_BUFFER_OVERFLOW.into(),
|
||||||
|
STATUS_BUFFER_TOO_SMALL.into(),
|
||||||
|
STATUS_INFO_LENGTH_MISMATCH.into(),
|
||||||
|
]
|
||||||
|
.contains(&err.code())
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut return_length = return_length.assume_init();
|
let mut return_length = return_length.assume_init();
|
||||||
let buf_len = (return_length as usize) / 2;
|
let buf_len = (return_length as usize) / 2;
|
||||||
let mut buffer: Vec<u16> = Vec::with_capacity(buf_len + 1);
|
let mut buffer: Vec<u16> = Vec::with_capacity(buf_len + 1);
|
||||||
buffer.set_len(buf_len);
|
if NtQueryInformationProcess(
|
||||||
|
|
||||||
status = NtQueryInformationProcess(
|
|
||||||
process_handle,
|
process_handle,
|
||||||
process_information_class,
|
process_information_class,
|
||||||
buffer.as_mut_ptr() as *mut _,
|
buffer.as_mut_ptr() as *mut _,
|
||||||
return_length,
|
return_length,
|
||||||
&mut return_length as *mut _,
|
&mut return_length as *mut _,
|
||||||
);
|
)
|
||||||
if !NT_SUCCESS(status) {
|
.is_err()
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
buffer.set_len(buf_len);
|
||||||
buffer.push(0);
|
buffer.push(0);
|
||||||
Some(buffer)
|
Some(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn get_cmdline_from_buffer(buffer: *const u16) -> Vec<String> {
|
unsafe fn get_cmdline_from_buffer(buffer: PCWSTR) -> Vec<String> {
|
||||||
// Get argc and argv from the command line
|
// Get argc and argv from the command line
|
||||||
let mut argc = MaybeUninit::<i32>::uninit();
|
let mut argc = MaybeUninit::<i32>::uninit();
|
||||||
let argv_p = winapi::um::shellapi::CommandLineToArgvW(buffer, argc.as_mut_ptr());
|
let argv_p = CommandLineToArgvW(buffer, argc.as_mut_ptr());
|
||||||
if argv_p.is_null() {
|
if argv_p.is_null() {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
@ -569,12 +582,10 @@ unsafe fn get_cmdline_from_buffer(buffer: *const u16) -> Vec<String> {
|
|||||||
|
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
for arg in argv {
|
for arg in argv {
|
||||||
let len = libc::wcslen(*arg);
|
res.push(String::from_utf16_lossy(arg.as_wide()));
|
||||||
let str_slice = std::slice::from_raw_parts(*arg, len);
|
|
||||||
res.push(String::from_utf16_lossy(str_slice));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
winapi::um::winbase::LocalFree(argv_p as *mut _);
|
let _err = LocalFree(HLOCAL(argv_p as _));
|
||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
@ -587,15 +598,16 @@ unsafe fn get_process_params(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// First check if target process is running in wow64 compatibility emulator
|
// First check if target process is running in wow64 compatibility emulator
|
||||||
let mut pwow32info = MaybeUninit::<LPVOID>::uninit();
|
let mut pwow32info = MaybeUninit::<*const c_void>::uninit();
|
||||||
let result = NtQueryInformationProcess(
|
if NtQueryInformationProcess(
|
||||||
handle,
|
handle,
|
||||||
ProcessWow64Information,
|
ProcessWow64Information,
|
||||||
pwow32info.as_mut_ptr() as *mut _,
|
pwow32info.as_mut_ptr().cast(),
|
||||||
size_of::<LPVOID>() as u32,
|
size_of::<*const c_void>() as u32,
|
||||||
null_mut(),
|
null_mut(),
|
||||||
);
|
)
|
||||||
if !NT_SUCCESS(result) {
|
.is_err()
|
||||||
|
{
|
||||||
return Err("Unable to check WOW64 information about the process");
|
return Err("Unable to check WOW64 information about the process");
|
||||||
}
|
}
|
||||||
let pwow32info = pwow32info.assume_init();
|
let pwow32info = pwow32info.assume_init();
|
||||||
@ -604,14 +616,15 @@ unsafe fn get_process_params(
|
|||||||
// target is a 64 bit process
|
// target is a 64 bit process
|
||||||
|
|
||||||
let mut pbasicinfo = MaybeUninit::<PROCESS_BASIC_INFORMATION>::uninit();
|
let mut pbasicinfo = MaybeUninit::<PROCESS_BASIC_INFORMATION>::uninit();
|
||||||
let result = NtQueryInformationProcess(
|
if NtQueryInformationProcess(
|
||||||
handle,
|
handle,
|
||||||
ProcessBasicInformation,
|
ProcessBasicInformation,
|
||||||
pbasicinfo.as_mut_ptr() as *mut _,
|
pbasicinfo.as_mut_ptr().cast(),
|
||||||
size_of::<PROCESS_BASIC_INFORMATION>() as u32,
|
size_of::<PROCESS_BASIC_INFORMATION>() as u32,
|
||||||
null_mut(),
|
null_mut(),
|
||||||
);
|
)
|
||||||
if !NT_SUCCESS(result) {
|
.is_err()
|
||||||
|
{
|
||||||
return Err("Unable to get basic process information");
|
return Err("Unable to get basic process information");
|
||||||
}
|
}
|
||||||
let pinfo = pbasicinfo.assume_init();
|
let pinfo = pbasicinfo.assume_init();
|
||||||
@ -619,11 +632,12 @@ unsafe fn get_process_params(
|
|||||||
let mut peb = MaybeUninit::<PEB>::uninit();
|
let mut peb = MaybeUninit::<PEB>::uninit();
|
||||||
if ReadProcessMemory(
|
if ReadProcessMemory(
|
||||||
handle,
|
handle,
|
||||||
pinfo.PebBaseAddress as *mut _,
|
pinfo.PebBaseAddress.cast(),
|
||||||
peb.as_mut_ptr() as *mut _,
|
peb.as_mut_ptr().cast(),
|
||||||
size_of::<PEB>() as SIZE_T,
|
size_of::<PEB>(),
|
||||||
std::ptr::null_mut(),
|
None,
|
||||||
) != TRUE
|
)
|
||||||
|
.is_err()
|
||||||
{
|
{
|
||||||
return Err("Unable to read process PEB");
|
return Err("Unable to read process PEB");
|
||||||
}
|
}
|
||||||
@ -633,11 +647,12 @@ unsafe fn get_process_params(
|
|||||||
let mut proc_params = MaybeUninit::<RTL_USER_PROCESS_PARAMETERS>::uninit();
|
let mut proc_params = MaybeUninit::<RTL_USER_PROCESS_PARAMETERS>::uninit();
|
||||||
if ReadProcessMemory(
|
if ReadProcessMemory(
|
||||||
handle,
|
handle,
|
||||||
peb.ProcessParameters as *mut PRTL_USER_PROCESS_PARAMETERS as *mut _,
|
peb.ProcessParameters.cast(),
|
||||||
proc_params.as_mut_ptr() as *mut _,
|
proc_params.as_mut_ptr().cast(),
|
||||||
size_of::<RTL_USER_PROCESS_PARAMETERS>() as SIZE_T,
|
size_of::<RTL_USER_PROCESS_PARAMETERS>(),
|
||||||
std::ptr::null_mut(),
|
None,
|
||||||
) != TRUE
|
)
|
||||||
|
.is_err()
|
||||||
{
|
{
|
||||||
return Err("Unable to read process parameters");
|
return Err("Unable to read process parameters");
|
||||||
}
|
}
|
||||||
@ -655,10 +670,11 @@ unsafe fn get_process_params(
|
|||||||
if ReadProcessMemory(
|
if ReadProcessMemory(
|
||||||
handle,
|
handle,
|
||||||
pwow32info,
|
pwow32info,
|
||||||
peb32.as_mut_ptr() as *mut _,
|
peb32.as_mut_ptr().cast(),
|
||||||
size_of::<PEB32>() as SIZE_T,
|
size_of::<PEB32>(),
|
||||||
std::ptr::null_mut(),
|
None,
|
||||||
) != TRUE
|
)
|
||||||
|
.is_err()
|
||||||
{
|
{
|
||||||
return Err("Unable to read PEB32");
|
return Err("Unable to read PEB32");
|
||||||
}
|
}
|
||||||
@ -667,11 +683,12 @@ unsafe fn get_process_params(
|
|||||||
let mut proc_params = MaybeUninit::<RTL_USER_PROCESS_PARAMETERS32>::uninit();
|
let mut proc_params = MaybeUninit::<RTL_USER_PROCESS_PARAMETERS32>::uninit();
|
||||||
if ReadProcessMemory(
|
if ReadProcessMemory(
|
||||||
handle,
|
handle,
|
||||||
peb32.ProcessParameters as *mut PRTL_USER_PROCESS_PARAMETERS32 as *mut _,
|
peb32.ProcessParameters as *mut _,
|
||||||
proc_params.as_mut_ptr() as *mut _,
|
proc_params.as_mut_ptr().cast(),
|
||||||
size_of::<RTL_USER_PROCESS_PARAMETERS32>() as SIZE_T,
|
size_of::<RTL_USER_PROCESS_PARAMETERS32>(),
|
||||||
std::ptr::null_mut(),
|
None,
|
||||||
) != TRUE
|
)
|
||||||
|
.is_err()
|
||||||
{
|
{
|
||||||
return Err("Unable to read 32 bit process parameters");
|
return Err("Unable to read 32 bit process parameters");
|
||||||
}
|
}
|
||||||
@ -683,13 +700,11 @@ unsafe fn get_process_params(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
static WINDOWS_8_1_OR_NEWER: Lazy<bool> = Lazy::new(|| {
|
static WINDOWS_8_1_OR_NEWER: Lazy<bool> = Lazy::new(|| unsafe {
|
||||||
let mut version_info: RTL_OSVERSIONINFOEXW = unsafe { MaybeUninit::zeroed().assume_init() };
|
let mut version_info: OSVERSIONINFOEXW = MaybeUninit::zeroed().assume_init();
|
||||||
|
|
||||||
version_info.dwOSVersionInfoSize = std::mem::size_of::<RTL_OSVERSIONINFOEXW>() as u32;
|
version_info.dwOSVersionInfoSize = std::mem::size_of::<OSVERSIONINFOEXW>() as u32;
|
||||||
if !NT_SUCCESS(unsafe {
|
if RtlGetVersion((&mut version_info as *mut OSVERSIONINFOEXW).cast()).is_err() {
|
||||||
RtlGetVersion(&mut version_info as *mut RTL_OSVERSIONINFOEXW as *mut _)
|
|
||||||
}) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -713,16 +728,16 @@ fn get_cmd_line_new(handle: HANDLE) -> Vec<String> {
|
|||||||
{
|
{
|
||||||
let buffer = (*(buffer.as_ptr() as *const UNICODE_STRING)).Buffer;
|
let buffer = (*(buffer.as_ptr() as *const UNICODE_STRING)).Buffer;
|
||||||
|
|
||||||
get_cmdline_from_buffer(buffer)
|
get_cmdline_from_buffer(PCWSTR::from_raw(buffer.as_ptr()))
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
Vec::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_cmd_line_old<T: RtlUserProcessParameters>(params: &T, handle: HANDLE) -> Vec<String> {
|
fn get_cmd_line_old<T: RtlUserProcessParameters>(params: &T, handle: HANDLE) -> Vec<String> {
|
||||||
match params.get_cmdline(handle) {
|
match params.get_cmdline(handle) {
|
||||||
Ok(buffer) => unsafe { get_cmdline_from_buffer(buffer.as_ptr()) },
|
Ok(buffer) => unsafe { get_cmdline_from_buffer(PCWSTR::from_raw(buffer.as_ptr())) },
|
||||||
Err(_e) => Vec::new(),
|
Err(_e) => Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -769,7 +784,7 @@ fn get_io(handle: HANDLE) -> Option<(u64, u64)> {
|
|||||||
let mut io: IO_COUNTERS = zeroed();
|
let mut io: IO_COUNTERS = zeroed();
|
||||||
let ret = GetProcessIoCounters(handle, &mut io);
|
let ret = GetProcessIoCounters(handle, &mut io);
|
||||||
|
|
||||||
if ret != 0 {
|
if ret.is_ok() {
|
||||||
Some((io.ReadTransferCount, io.WriteTransferCount))
|
Some((io.ReadTransferCount, io.WriteTransferCount))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -790,7 +805,7 @@ fn get_user(handle: HANDLE) -> Option<SidName> {
|
|||||||
let mut token: HANDLE = zeroed();
|
let mut token: HANDLE = zeroed();
|
||||||
let ret = OpenProcessToken(handle, TOKEN_QUERY, &mut token);
|
let ret = OpenProcessToken(handle, TOKEN_QUERY, &mut token);
|
||||||
|
|
||||||
if ret == 0 {
|
if ret.is_err() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -798,7 +813,7 @@ fn get_user(handle: HANDLE) -> Option<SidName> {
|
|||||||
let _ = GetTokenInformation(
|
let _ = GetTokenInformation(
|
||||||
token,
|
token,
|
||||||
TokenUser,
|
TokenUser,
|
||||||
ptr::null::<c_void>() as *mut c_void,
|
Some(ptr::null::<c_void>() as *mut c_void),
|
||||||
0,
|
0,
|
||||||
&mut cb_needed,
|
&mut cb_needed,
|
||||||
);
|
);
|
||||||
@ -808,13 +823,13 @@ fn get_user(handle: HANDLE) -> Option<SidName> {
|
|||||||
let ret = GetTokenInformation(
|
let ret = GetTokenInformation(
|
||||||
token,
|
token,
|
||||||
TokenUser,
|
TokenUser,
|
||||||
buf.as_mut_ptr() as *mut c_void,
|
Some(buf.as_mut_ptr() as *mut c_void),
|
||||||
cb_needed,
|
cb_needed,
|
||||||
&mut cb_needed,
|
&mut cb_needed,
|
||||||
);
|
);
|
||||||
buf.set_len(cb_needed as usize);
|
buf.set_len(cb_needed as usize);
|
||||||
|
|
||||||
if ret == 0 {
|
if ret.is_err() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -843,7 +858,7 @@ fn get_groups(handle: HANDLE) -> Option<Vec<SidName>> {
|
|||||||
let mut token: HANDLE = zeroed();
|
let mut token: HANDLE = zeroed();
|
||||||
let ret = OpenProcessToken(handle, TOKEN_QUERY, &mut token);
|
let ret = OpenProcessToken(handle, TOKEN_QUERY, &mut token);
|
||||||
|
|
||||||
if ret == 0 {
|
if ret.is_err() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -851,7 +866,7 @@ fn get_groups(handle: HANDLE) -> Option<Vec<SidName>> {
|
|||||||
let _ = GetTokenInformation(
|
let _ = GetTokenInformation(
|
||||||
token,
|
token,
|
||||||
TokenGroups,
|
TokenGroups,
|
||||||
ptr::null::<c_void>() as *mut c_void,
|
Some(ptr::null::<c_void>() as *mut c_void),
|
||||||
0,
|
0,
|
||||||
&mut cb_needed,
|
&mut cb_needed,
|
||||||
);
|
);
|
||||||
@ -861,13 +876,13 @@ fn get_groups(handle: HANDLE) -> Option<Vec<SidName>> {
|
|||||||
let ret = GetTokenInformation(
|
let ret = GetTokenInformation(
|
||||||
token,
|
token,
|
||||||
TokenGroups,
|
TokenGroups,
|
||||||
buf.as_mut_ptr() as *mut c_void,
|
Some(buf.as_mut_ptr() as *mut c_void),
|
||||||
cb_needed,
|
cb_needed,
|
||||||
&mut cb_needed,
|
&mut cb_needed,
|
||||||
);
|
);
|
||||||
buf.set_len(cb_needed as usize);
|
buf.set_len(cb_needed as usize);
|
||||||
|
|
||||||
if ret == 0 {
|
if ret.is_err() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -901,7 +916,7 @@ fn get_groups(handle: HANDLE) -> Option<Vec<SidName>> {
|
|||||||
fn get_sid(psid: PSID) -> Vec<u64> {
|
fn get_sid(psid: PSID) -> Vec<u64> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut ret = Vec::new();
|
let mut ret = Vec::new();
|
||||||
let psid = psid as *const SID;
|
let psid = psid.0 as *const SID;
|
||||||
|
|
||||||
let mut ia = 0;
|
let mut ia = 0;
|
||||||
ia |= u64::from((*psid).IdentifierAuthority.Value[0]) << 40;
|
ia |= u64::from((*psid).IdentifierAuthority.Value[0]) << 40;
|
||||||
@ -924,7 +939,7 @@ fn get_sid(psid: PSID) -> Vec<u64> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
thread_local!(
|
thread_local!(
|
||||||
pub static NAME_CACHE: RefCell<HashMap<PSID, Option<(String, String)>>> =
|
pub static NAME_CACHE: RefCell<HashMap<*mut c_void, Option<(String, String)>>> =
|
||||||
RefCell::new(HashMap::new());
|
RefCell::new(HashMap::new());
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -932,11 +947,11 @@ thread_local!(
|
|||||||
fn get_name_cached(psid: PSID) -> Option<(String, String)> {
|
fn get_name_cached(psid: PSID) -> Option<(String, String)> {
|
||||||
NAME_CACHE.with(|c| {
|
NAME_CACHE.with(|c| {
|
||||||
let mut c = c.borrow_mut();
|
let mut c = c.borrow_mut();
|
||||||
if let Some(x) = c.get(&psid) {
|
if let Some(x) = c.get(&psid.0) {
|
||||||
x.clone()
|
x.clone()
|
||||||
} else {
|
} else {
|
||||||
let x = get_name(psid);
|
let x = get_name(psid);
|
||||||
c.insert(psid, x.clone());
|
c.insert(psid.0, x.clone());
|
||||||
x
|
x
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -947,13 +962,13 @@ fn get_name(psid: PSID) -> Option<(String, String)> {
|
|||||||
unsafe {
|
unsafe {
|
||||||
let mut cc_name = 0;
|
let mut cc_name = 0;
|
||||||
let mut cc_domainname = 0;
|
let mut cc_domainname = 0;
|
||||||
let mut pe_use = 0;
|
let mut pe_use = SID_NAME_USE::default();
|
||||||
let _ = LookupAccountSidW(
|
let _ = LookupAccountSidW(
|
||||||
ptr::null::<u16>() as *mut u16,
|
PCWSTR::null(),
|
||||||
psid,
|
psid,
|
||||||
ptr::null::<u16>() as *mut u16,
|
PWSTR::null(),
|
||||||
&mut cc_name,
|
&mut cc_name,
|
||||||
ptr::null::<u16>() as *mut u16,
|
PWSTR::null(),
|
||||||
&mut cc_domainname,
|
&mut cc_domainname,
|
||||||
&mut pe_use,
|
&mut pe_use,
|
||||||
);
|
);
|
||||||
@ -966,17 +981,17 @@ fn get_name(psid: PSID) -> Option<(String, String)> {
|
|||||||
let mut domainname: Vec<u16> = Vec::with_capacity(cc_domainname as usize);
|
let mut domainname: Vec<u16> = Vec::with_capacity(cc_domainname as usize);
|
||||||
name.set_len(cc_name as usize);
|
name.set_len(cc_name as usize);
|
||||||
domainname.set_len(cc_domainname as usize);
|
domainname.set_len(cc_domainname as usize);
|
||||||
let ret = LookupAccountSidW(
|
if LookupAccountSidW(
|
||||||
ptr::null::<u16>() as *mut u16,
|
PCWSTR::null(),
|
||||||
psid,
|
psid,
|
||||||
name.as_mut_ptr(),
|
PWSTR::from_raw(name.as_mut_ptr()),
|
||||||
&mut cc_name,
|
&mut cc_name,
|
||||||
domainname.as_mut_ptr(),
|
PWSTR::from_raw(domainname.as_mut_ptr()),
|
||||||
&mut cc_domainname,
|
&mut cc_domainname,
|
||||||
&mut pe_use,
|
&mut pe_use,
|
||||||
);
|
)
|
||||||
|
.is_err()
|
||||||
if ret == 0 {
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ pub fn pipeline(commands: &str) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn nu_repl_code(source_lines: &[&str]) -> String {
|
pub fn nu_repl_code(source_lines: &[&str]) -> String {
|
||||||
let mut out = String::from("nu --testbin=nu_repl [ ");
|
let mut out = String::from("nu --testbin=nu_repl ...[ ");
|
||||||
|
|
||||||
for line in source_lines.iter() {
|
for line in source_lines.iter() {
|
||||||
// convert each "line" to really be a single line to prevent nu! macro joining the newlines
|
// convert each "line" to really be a single line to prevent nu! macro joining the newlines
|
||||||
|
@ -14,5 +14,5 @@ nu-protocol = { path = "../nu-protocol", version = "0.88.2", features = ["plugin
|
|||||||
|
|
||||||
indexmap = "2.1"
|
indexmap = "2.1"
|
||||||
eml-parser = "0.1"
|
eml-parser = "0.1"
|
||||||
ical = "0.8"
|
ical = "0.9"
|
||||||
rust-ini = "0.20.0"
|
rust-ini = "0.20.0"
|
||||||
|
@ -16,4 +16,4 @@ profile = "default"
|
|||||||
# use in nushell, we may opt to use the bleeding edge stable version of rust.
|
# use in nushell, we may opt to use the bleeding edge stable version of rust.
|
||||||
# I believe rust is on a 6 week release cycle and nushell is on a 4 week release cycle.
|
# I believe rust is on a 6 week release cycle and nushell is on a 4 week release cycle.
|
||||||
# So, every two nushell releases, this version number should be bumped by one.
|
# So, every two nushell releases, this version number should be bumped by one.
|
||||||
channel = "1.72.1"
|
channel = "1.73.0"
|
||||||
|
@ -99,8 +99,8 @@ fn known_external_misc_values() -> TestResult {
|
|||||||
run_test(
|
run_test(
|
||||||
r#"
|
r#"
|
||||||
let x = 'abc'
|
let x = 'abc'
|
||||||
extern echo []
|
extern echo [...args]
|
||||||
echo $x [ a b c ]
|
echo $x ...[ a b c ]
|
||||||
"#,
|
"#,
|
||||||
"abc a b c",
|
"abc a b c",
|
||||||
)
|
)
|
||||||
|
@ -763,3 +763,14 @@ fn properly_typecheck_rest_param() -> TestResult {
|
|||||||
fn implied_collect_has_compatible_type() -> TestResult {
|
fn implied_collect_has_compatible_type() -> TestResult {
|
||||||
run_test(r#"let idx = 3 | $in; $idx < 1"#, "false")
|
run_test(r#"let idx = 3 | $in; $idx < 1"#, "false")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn record_expected_colon() -> TestResult {
|
||||||
|
fail_test(r#"{ a: 2 b }"#, "expected ':'")?;
|
||||||
|
fail_test(r#"{ a: 2 b 3 }"#, "expected ':'")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn record_missing_value() -> TestResult {
|
||||||
|
fail_test(r#"{ a: 2 b: }"#, "expected value for record field")
|
||||||
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::tests::{fail_test, run_test, TestResult};
|
use crate::tests::{fail_test, run_test, TestResult};
|
||||||
|
use nu_test_support::nu;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn spread_in_list() -> TestResult {
|
fn spread_in_list() -> TestResult {
|
||||||
@ -24,30 +25,6 @@ fn spread_in_list() -> TestResult {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn const_spread_in_list() -> TestResult {
|
|
||||||
run_test(r#"const x = [...[]]; $x | to nuon"#, "[]").unwrap();
|
|
||||||
run_test(
|
|
||||||
r#"const x = [1 2 ...[[3] {x: 1}] 5]; $x | to nuon"#,
|
|
||||||
"[1, 2, [3], {x: 1}, 5]",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
run_test(
|
|
||||||
r#"const x = [...([f o o]) 10]; $x | to nuon"#,
|
|
||||||
"[f, o, o, 10]",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
run_test(
|
|
||||||
r#"const l = [1, 2, [3]]; const x = [...$l $l]; $x | to nuon"#,
|
|
||||||
"[1, 2, [3], [1, 2, [3]]]",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
run_test(
|
|
||||||
r#"[ ...[ ...[ ...[ a ] b ] c ] d ] | to nuon"#,
|
|
||||||
"[a, b, c, d]",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn not_spread() -> TestResult {
|
fn not_spread() -> TestResult {
|
||||||
run_test(r#"def ... [x] { $x }; ... ..."#, "...").unwrap();
|
run_test(r#"def ... [x] { $x }; ... ..."#, "...").unwrap();
|
||||||
@ -95,15 +72,6 @@ fn spread_in_record() -> TestResult {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn const_spread_in_record() -> TestResult {
|
|
||||||
run_test(r#"const x = {...{...{...{}}}}; $x | to nuon"#, "{}").unwrap();
|
|
||||||
run_test(
|
|
||||||
r#"const x = {foo: bar ...{a: {x: 1}} b: 3}; $x | to nuon"#,
|
|
||||||
"{foo: bar, a: {x: 1}, b: 3}",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn duplicate_cols() -> TestResult {
|
fn duplicate_cols() -> TestResult {
|
||||||
fail_test(r#"{a: 1, ...{a: 3}}"#, "column used twice").unwrap();
|
fail_test(r#"{a: 1, ...{a: 3}}"#, "column used twice").unwrap();
|
||||||
@ -111,16 +79,6 @@ fn duplicate_cols() -> TestResult {
|
|||||||
fail_test(r#"{...{a: 0, x: 2}, ...{x: 5}}"#, "column used twice")
|
fail_test(r#"{...{a: 0, x: 2}, ...{x: 5}}"#, "column used twice")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn const_duplicate_cols() -> TestResult {
|
|
||||||
fail_test(r#"const _ = {a: 1, ...{a: 3}}"#, "column used twice").unwrap();
|
|
||||||
fail_test(r#"const _ = {...{a: 4, x: 3}, x: 1}"#, "column used twice").unwrap();
|
|
||||||
fail_test(
|
|
||||||
r#"const _ = {...{a: 0, x: 2}, ...{x: 5}}"#,
|
|
||||||
"column used twice",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bad_spread_on_non_record() -> TestResult {
|
fn bad_spread_on_non_record() -> TestResult {
|
||||||
fail_test(r#"let x = 5; { ...$x }"#, "cannot spread").unwrap();
|
fail_test(r#"let x = 5; { ...$x }"#, "cannot spread").unwrap();
|
||||||
@ -139,3 +97,96 @@ fn spread_type_record() -> TestResult {
|
|||||||
"type_mismatch",
|
"type_mismatch",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn spread_external_args() {
|
||||||
|
assert_eq!(
|
||||||
|
nu!(r#"nu --testbin cococo ...[1 "foo"] 2 ...[3 "bar"]"#).out,
|
||||||
|
"1 foo 2 3 bar",
|
||||||
|
);
|
||||||
|
// exec doesn't have rest parameters but allows unknown arguments
|
||||||
|
assert_eq!(
|
||||||
|
nu!(r#"exec nu --testbin cococo "foo" ...[5 6]"#).out,
|
||||||
|
"foo 5 6"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn spread_internal_args() -> TestResult {
|
||||||
|
run_test(
|
||||||
|
r#"
|
||||||
|
let list = ["foo" 4]
|
||||||
|
def f [a b c? d? ...x] { [$a $b $c $d $x] | to nuon }
|
||||||
|
f 1 2 ...[5 6] 7 ...$list"#,
|
||||||
|
"[1, 2, null, null, [5, 6, 7, foo, 4]]",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
run_test(
|
||||||
|
r#"
|
||||||
|
def f [a b c? d? ...x] { [$a $b $c $d $x] | to nuon }
|
||||||
|
f 1 2 3 ...[5 6]"#,
|
||||||
|
"[1, 2, 3, null, [5, 6]]",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
run_test(
|
||||||
|
r#"
|
||||||
|
def f [--flag: int ...x] { [$flag $x] | to nuon }
|
||||||
|
f 2 ...[foo] 4 --flag 5 6 ...[7 8]"#,
|
||||||
|
"[5, [2, foo, 4, 6, 7, 8]]",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
run_test(
|
||||||
|
r#"
|
||||||
|
def f [a b? --flag: int ...x] { [$a $b $flag $x] | to nuon }
|
||||||
|
f 1 ...[foo] 4 --flag 5 6 ...[7 8]"#,
|
||||||
|
"[1, null, 5, [foo, 4, 6, 7, 8]]",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bad_spread_internal_args() -> TestResult {
|
||||||
|
fail_test(
|
||||||
|
r#"
|
||||||
|
def f [a b c? d? ...x] { echo $a $b $c $d $x }
|
||||||
|
f 1 ...[5 6]"#,
|
||||||
|
"Missing required positional argument",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
fail_test(
|
||||||
|
r#"
|
||||||
|
def f [a b?] { echo a b c d }
|
||||||
|
f ...[5 6]"#,
|
||||||
|
"unexpected spread argument",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn spread_non_list_args() {
|
||||||
|
fail_test(r#"echo ...(1)"#, "cannot spread value").unwrap();
|
||||||
|
assert!(nu!(r#"nu --testbin cococo ...(1)"#)
|
||||||
|
.err
|
||||||
|
.contains("cannot spread value"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn spread_args_type() -> TestResult {
|
||||||
|
fail_test(r#"def f [...x: int] {}; f ...["abc"]"#, "expected int")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn explain_spread_args() -> TestResult {
|
||||||
|
run_test(
|
||||||
|
r#"(explain { || echo ...[1 2] }).cmd_args.0 | select arg_type name type | to nuon"#,
|
||||||
|
r#"[[arg_type, name, type]; [spread, "[1 2]", list<int>]]"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deprecate_implicit_spread_for_externals() {
|
||||||
|
// TODO: When automatic spreading is removed, test that list literals fail at parse time
|
||||||
|
let result = nu!(r#"nu --testbin cococo [1 2]"#);
|
||||||
|
assert!(result
|
||||||
|
.err
|
||||||
|
.contains("Automatically spreading lists is deprecated"));
|
||||||
|
assert_eq!(result.out, "1 2");
|
||||||
|
}
|
||||||
|
@ -304,6 +304,19 @@ fn const_captures_work() {
|
|||||||
assert_eq!(actual.out, "xy");
|
assert_eq!(actual.out, "xy");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn const_captures_in_closures_work() {
|
||||||
|
let module = "module foo {
|
||||||
|
const a = 'world'
|
||||||
|
export def bar [] {
|
||||||
|
'hello ' + $a
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
let inp = &[module, "use foo", "do { foo bar }"];
|
||||||
|
let actual = nu!(&inp.join("; "));
|
||||||
|
assert_eq!(actual.out, "hello world");
|
||||||
|
}
|
||||||
|
|
||||||
#[ignore = "TODO: Need to fix `overlay hide` to hide the constants brough by `overlay use`"]
|
#[ignore = "TODO: Need to fix `overlay hide` to hide the constants brough by `overlay use`"]
|
||||||
#[test]
|
#[test]
|
||||||
fn complex_const_overlay_use_hide() {
|
fn complex_const_overlay_use_hide() {
|
||||||
|
@ -85,7 +85,7 @@ fn execute_binary_in_string() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn single_quote_dollar_external() {
|
fn single_quote_dollar_external() {
|
||||||
let actual = nu!("let author = 'JT'; ^echo $'foo=($author)'");
|
let actual = nu!("let author = 'JT'; nu --testbin cococo $'foo=($author)'");
|
||||||
|
|
||||||
assert_eq!(actual.out, "foo=JT");
|
assert_eq!(actual.out, "foo=JT");
|
||||||
}
|
}
|
||||||
@ -354,7 +354,7 @@ mod nu_commands {
|
|||||||
#[test]
|
#[test]
|
||||||
fn command_list_arg_test() {
|
fn command_list_arg_test() {
|
||||||
let actual = nu!("
|
let actual = nu!("
|
||||||
nu ['-c' 'version']
|
nu ...['-c' 'version']
|
||||||
");
|
");
|
||||||
|
|
||||||
assert!(actual.out.contains("version"));
|
assert!(actual.out.contains("version"));
|
||||||
@ -365,7 +365,7 @@ mod nu_commands {
|
|||||||
#[test]
|
#[test]
|
||||||
fn command_cell_path_arg_test() {
|
fn command_cell_path_arg_test() {
|
||||||
let actual = nu!("
|
let actual = nu!("
|
||||||
nu ([ '-c' 'version' ])
|
nu ...([ '-c' 'version' ])
|
||||||
");
|
");
|
||||||
|
|
||||||
assert!(actual.out.contains("version"));
|
assert!(actual.out.contains("version"));
|
||||||
@ -436,7 +436,7 @@ mod external_command_arguments {
|
|||||||
let actual = nu!(
|
let actual = nu!(
|
||||||
cwd: dirs.test(), pipeline(
|
cwd: dirs.test(), pipeline(
|
||||||
"
|
"
|
||||||
nu --testbin cococo (ls | get name)
|
nu --testbin cococo ...(ls | get name)
|
||||||
"
|
"
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -493,18 +493,16 @@ mod external_command_arguments {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn semicolons_are_sanitized_before_passing_to_subshell() {
|
fn semicolons_are_sanitized_before_passing_to_subshell() {
|
||||||
let actual = nu!("^echo \"a;b\"");
|
let actual = nu!("nu --testbin cococo \"a;b\"");
|
||||||
|
|
||||||
assert_eq!(actual.out, "a;b");
|
assert_eq!(actual.out, "a;b");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ampersands_are_sanitized_before_passing_to_subshell() {
|
fn ampersands_are_sanitized_before_passing_to_subshell() {
|
||||||
let actual = nu!("^echo \"a&b\"");
|
let actual = nu!("nu --testbin cococo \"a&b\"");
|
||||||
|
|
||||||
assert_eq!(actual.out, "a&b");
|
assert_eq!(actual.out, "a&b");
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user