forked from extern/nushell
Cratification: Break out nu_cmd_lang into a separate crate (#8181)
# Description This breaks out the core_commands into a separate crate called nu_cmd_lang _(Thank you for improving Nushell. Please, check our [contributing guide](../CONTRIBUTING.md) and talk to the core team before making major changes.)_ _(Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience.)_ # User-Facing Changes _(List of all changes that impact the user experience here. This helps us keep track of breaking changes.)_ # Tests + Formatting Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: - `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass # After Submitting If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date.
This commit is contained in:
parent
d0aefa99eb
commit
585e104608
40
Cargo.lock
generated
40
Cargo.lock
generated
@ -2561,6 +2561,7 @@ dependencies = [
|
|||||||
"nix",
|
"nix",
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
"nu-cli",
|
"nu-cli",
|
||||||
|
"nu-cmd-lang",
|
||||||
"nu-color-config",
|
"nu-color-config",
|
||||||
"nu-command",
|
"nu-command",
|
||||||
"nu-engine",
|
"nu-engine",
|
||||||
@ -2627,6 +2628,43 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nu-cmd-lang"
|
||||||
|
version = "0.76.1"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"chrono",
|
||||||
|
"crossterm 0.24.0",
|
||||||
|
"fancy-regex",
|
||||||
|
"itertools",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"lscolors",
|
||||||
|
"nu-ansi-term",
|
||||||
|
"nu-color-config",
|
||||||
|
"nu-engine",
|
||||||
|
"nu-explore",
|
||||||
|
"nu-json",
|
||||||
|
"nu-parser",
|
||||||
|
"nu-path",
|
||||||
|
"nu-pretty-hex",
|
||||||
|
"nu-protocol",
|
||||||
|
"nu-system",
|
||||||
|
"nu-table",
|
||||||
|
"nu-term-grid",
|
||||||
|
"nu-test-support",
|
||||||
|
"nu-utils",
|
||||||
|
"once_cell",
|
||||||
|
"pathdiff",
|
||||||
|
"rayon",
|
||||||
|
"serde",
|
||||||
|
"shadow-rs",
|
||||||
|
"sysinfo 0.27.7",
|
||||||
|
"tabled",
|
||||||
|
"terminal_size 0.2.1",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-color-config"
|
name = "nu-color-config"
|
||||||
version = "0.76.1"
|
version = "0.76.1"
|
||||||
@ -2680,6 +2718,7 @@ dependencies = [
|
|||||||
"mime_guess",
|
"mime_guess",
|
||||||
"notify",
|
"notify",
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
|
"nu-cmd-lang",
|
||||||
"nu-color-config",
|
"nu-color-config",
|
||||||
"nu-engine",
|
"nu-engine",
|
||||||
"nu-explore",
|
"nu-explore",
|
||||||
@ -5008,6 +5047,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"ntapi",
|
"ntapi",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"rayon",
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ members = [
|
|||||||
"crates/nu-engine",
|
"crates/nu-engine",
|
||||||
"crates/nu-parser",
|
"crates/nu-parser",
|
||||||
"crates/nu-system",
|
"crates/nu-system",
|
||||||
|
"crates/nu-cmd-lang",
|
||||||
"crates/nu-command",
|
"crates/nu-command",
|
||||||
"crates/nu-protocol",
|
"crates/nu-protocol",
|
||||||
"crates/nu-plugin",
|
"crates/nu-plugin",
|
||||||
@ -48,6 +49,7 @@ miette = { version = "5.5.0", features = ["fancy-no-backtrace"] }
|
|||||||
nu-ansi-term = "0.46.0"
|
nu-ansi-term = "0.46.0"
|
||||||
nu-cli = { path = "./crates/nu-cli", version = "0.76.1" }
|
nu-cli = { path = "./crates/nu-cli", version = "0.76.1" }
|
||||||
nu-color-config = { path = "./crates/nu-color-config", version = "0.76.1" }
|
nu-color-config = { path = "./crates/nu-color-config", version = "0.76.1" }
|
||||||
|
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.76.1" }
|
||||||
nu-command = { path = "./crates/nu-command", version = "0.76.1" }
|
nu-command = { path = "./crates/nu-command", version = "0.76.1" }
|
||||||
nu-engine = { path = "./crates/nu-engine", version = "0.76.1" }
|
nu-engine = { path = "./crates/nu-engine", version = "0.76.1" }
|
||||||
nu-json = { path = "./crates/nu-json", version = "0.76.1" }
|
nu-json = { path = "./crates/nu-json", version = "0.76.1" }
|
||||||
|
55
crates/nu-cmd-lang/Cargo.toml
Normal file
55
crates/nu-cmd-lang/Cargo.toml
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["The Nushell Project Developers"]
|
||||||
|
build = "build.rs"
|
||||||
|
description = "Nushell's core language commands"
|
||||||
|
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
|
||||||
|
edition = "2021"
|
||||||
|
license = "MIT"
|
||||||
|
name = "nu-cmd-lang"
|
||||||
|
version = "0.76.1"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
bench = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = { version="1.0.123", features=["derive"] }
|
||||||
|
# used only for text_style Alignments
|
||||||
|
tabled = { version = "0.10.0", features = ["color"], default-features = false }
|
||||||
|
|
||||||
|
nu-ansi-term = "0.46.0"
|
||||||
|
nu-color-config = { path = "../nu-color-config", version = "0.76.1" }
|
||||||
|
nu-engine = { path = "../nu-engine", version = "0.76.1" }
|
||||||
|
nu-explore = { path = "../nu-explore", version = "0.76.1" }
|
||||||
|
nu-json = { path="../nu-json", version = "0.76.1" }
|
||||||
|
nu-parser = { path = "../nu-parser", version = "0.76.1" }
|
||||||
|
nu-path = { path = "../nu-path", version = "0.76.1" }
|
||||||
|
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.76.1" }
|
||||||
|
nu-protocol = { path = "../nu-protocol", version = "0.76.1" }
|
||||||
|
nu-system = { path = "../nu-system", version = "0.76.1" }
|
||||||
|
nu-table = { path = "../nu-table", version = "0.76.1" }
|
||||||
|
nu-term-grid = { path = "../nu-term-grid", version = "0.76.1" }
|
||||||
|
nu-utils = { path = "../nu-utils", version = "0.76.1" }
|
||||||
|
|
||||||
|
atty = "0.2.14"
|
||||||
|
chrono = { version = "0.4.23", features = ["std", "unstable-locales"], default-features = false }
|
||||||
|
crossterm = "0.24.0"
|
||||||
|
fancy-regex = "0.11.0"
|
||||||
|
itertools = "0.10.0"
|
||||||
|
log = "0.4.14"
|
||||||
|
lscolors = { version = "0.12.0", features = ["crossterm"], default-features = false }
|
||||||
|
once_cell = "1.17"
|
||||||
|
pathdiff = "0.2.1"
|
||||||
|
rayon = "1.6.1"
|
||||||
|
shadow-rs = { version = "0.20.0", default-features = false }
|
||||||
|
sysinfo = "0.27.7"
|
||||||
|
terminal_size = "0.2.1"
|
||||||
|
url = "2.2.1"
|
||||||
|
|
||||||
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
libc = "0.2"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
shadow-rs = { version = "0.20.0", default-features = false }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
nu-test-support = { path="../nu-test-support", version = "0.76.1" }
|
21
crates/nu-cmd-lang/LICENSE
Normal file
21
crates/nu-cmd-lang/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 - 2022 The Nushell Project Developers
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
20
crates/nu-cmd-lang/build.rs
Normal file
20
crates/nu-cmd-lang/build.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
fn main() -> shadow_rs::SdResult<()> {
|
||||||
|
// Look up the current Git commit ourselves instead of relying on shadow_rs,
|
||||||
|
// because shadow_rs does it in a really slow-to-compile way (it builds libgit2)
|
||||||
|
let hash = get_git_hash().unwrap_or_default();
|
||||||
|
println!("cargo:rustc-env=NU_COMMIT_HASH={hash}");
|
||||||
|
|
||||||
|
shadow_rs::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_git_hash() -> Option<String> {
|
||||||
|
Command::new("git")
|
||||||
|
.args(["rev-parse", "HEAD"])
|
||||||
|
.output()
|
||||||
|
.ok()
|
||||||
|
.filter(|output| output.status.success())
|
||||||
|
.and_then(|output| String::from_utf8(output.stdout).ok())
|
||||||
|
.map(|hash| hash.trim().to_string())
|
||||||
|
}
|
@ -76,6 +76,7 @@ impl Command for Describe {
|
|||||||
example: "'hello' | describe",
|
example: "'hello' | describe",
|
||||||
result: Some(Value::test_string("string")),
|
result: Some(Value::test_string("string")),
|
||||||
},
|
},
|
||||||
|
/*
|
||||||
Example {
|
Example {
|
||||||
description: "Describe a stream of data, collecting it first",
|
description: "Describe a stream of data, collecting it first",
|
||||||
example: "[1 2 3] | each {|i| $i} | describe",
|
example: "[1 2 3] | each {|i| $i} | describe",
|
||||||
@ -86,6 +87,7 @@ impl Command for Describe {
|
|||||||
example: "[1 2 3] | each {|i| $i} | describe --no-collect",
|
example: "[1 2 3] | each {|i| $i} | describe --no-collect",
|
||||||
result: Some(Value::test_string("stream")),
|
result: Some(Value::test_string("stream")),
|
||||||
},
|
},
|
||||||
|
*/
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -75,8 +75,8 @@ pub use try_::Try;
|
|||||||
pub use use_::Use;
|
pub use use_::Use;
|
||||||
pub use version::Version;
|
pub use version::Version;
|
||||||
pub use while_::While;
|
pub use while_::While;
|
||||||
#[cfg(feature = "plugin")]
|
//#[cfg(feature = "plugin")]
|
||||||
mod register;
|
mod register;
|
||||||
|
|
||||||
#[cfg(feature = "plugin")]
|
//#[cfg(feature = "plugin")]
|
||||||
pub use register::Register;
|
pub use register::Register;
|
74
crates/nu-cmd-lang/src/default_context.rs
Normal file
74
crates/nu-cmd-lang/src/default_context.rs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
||||||
|
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
pub fn create_default_context() -> EngineState {
|
||||||
|
let mut engine_state = EngineState::new();
|
||||||
|
|
||||||
|
let delta = {
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
macro_rules! bind_command {
|
||||||
|
( $( $command:expr ),* $(,)? ) => {
|
||||||
|
$( working_set.add_decl(Box::new($command)); )*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Core
|
||||||
|
bind_command! {
|
||||||
|
Alias,
|
||||||
|
Break,
|
||||||
|
Commandline,
|
||||||
|
Const,
|
||||||
|
Continue,
|
||||||
|
Def,
|
||||||
|
DefEnv,
|
||||||
|
Describe,
|
||||||
|
Do,
|
||||||
|
Echo,
|
||||||
|
ErrorMake,
|
||||||
|
ExportAlias,
|
||||||
|
ExportCommand,
|
||||||
|
ExportDef,
|
||||||
|
ExportDefEnv,
|
||||||
|
ExportExtern,
|
||||||
|
ExportUse,
|
||||||
|
Extern,
|
||||||
|
For,
|
||||||
|
Help,
|
||||||
|
HelpAliases,
|
||||||
|
HelpCommands,
|
||||||
|
HelpModules,
|
||||||
|
HelpOperators,
|
||||||
|
Hide,
|
||||||
|
HideEnv,
|
||||||
|
If,
|
||||||
|
Ignore,
|
||||||
|
Overlay,
|
||||||
|
OverlayUse,
|
||||||
|
OverlayList,
|
||||||
|
OverlayNew,
|
||||||
|
OverlayHide,
|
||||||
|
Let,
|
||||||
|
Loop,
|
||||||
|
Module,
|
||||||
|
Mut,
|
||||||
|
Return,
|
||||||
|
Try,
|
||||||
|
Use,
|
||||||
|
Version,
|
||||||
|
While,
|
||||||
|
};
|
||||||
|
|
||||||
|
//#[cfg(feature = "plugin")]
|
||||||
|
bind_command!(Register);
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(err) = engine_state.merge_delta(delta) {
|
||||||
|
eprintln!("Error creating default context: {err:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
}
|
298
crates/nu-cmd-lang/src/example_test.rs
Normal file
298
crates/nu-cmd-lang/src/example_test.rs
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
use nu_protocol::engine::Command;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn test_examples(cmd: impl Command + 'static) {
|
||||||
|
test_examples::test_examples(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_examples {
|
||||||
|
use crate::{Break, Describe, Mut};
|
||||||
|
use crate::{Echo, If, Let};
|
||||||
|
use itertools::Itertools;
|
||||||
|
use nu_protocol::{
|
||||||
|
ast::Block,
|
||||||
|
engine::{Command, EngineState, Stack, StateDelta, StateWorkingSet},
|
||||||
|
Example, PipelineData, Signature, Span, Type, Value,
|
||||||
|
};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
pub fn test_examples(cmd: impl Command + 'static) {
|
||||||
|
let examples = cmd.examples();
|
||||||
|
let signature = cmd.signature();
|
||||||
|
let mut engine_state = make_engine_state(cmd.clone_box());
|
||||||
|
|
||||||
|
let cwd = std::env::current_dir().expect("Could not get current working directory.");
|
||||||
|
|
||||||
|
let mut witnessed_type_transformations = HashSet::<(Type, Type)>::new();
|
||||||
|
|
||||||
|
for example in examples {
|
||||||
|
if example.result.is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
witnessed_type_transformations.extend(
|
||||||
|
check_example_input_and_output_types_match_command_signature(
|
||||||
|
&example,
|
||||||
|
&cwd,
|
||||||
|
&mut make_engine_state(cmd.clone_box()),
|
||||||
|
&signature.input_output_types,
|
||||||
|
signature.operates_on_cell_paths(),
|
||||||
|
signature.vectorizes_over_list,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
check_example_evaluates_to_expected_output(&example, cwd.as_path(), &mut engine_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
check_all_signature_input_output_types_entries_have_examples(
|
||||||
|
signature,
|
||||||
|
witnessed_type_transformations,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_engine_state(cmd: Box<dyn Command>) -> Box<EngineState> {
|
||||||
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
|
||||||
|
let delta = {
|
||||||
|
// Base functions that are needed for testing
|
||||||
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
working_set.add_decl(Box::new(Break));
|
||||||
|
working_set.add_decl(Box::new(Describe));
|
||||||
|
working_set.add_decl(Box::new(Echo));
|
||||||
|
working_set.add_decl(Box::new(If));
|
||||||
|
working_set.add_decl(Box::new(Let));
|
||||||
|
working_set.add_decl(Box::new(Mut));
|
||||||
|
|
||||||
|
// Adding the command that is being tested to the working set
|
||||||
|
working_set.add_decl(cmd);
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
.merge_delta(delta)
|
||||||
|
.expect("Error merging delta");
|
||||||
|
engine_state
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_example_input_and_output_types_match_command_signature(
|
||||||
|
example: &Example,
|
||||||
|
cwd: &std::path::Path,
|
||||||
|
engine_state: &mut Box<EngineState>,
|
||||||
|
signature_input_output_types: &Vec<(Type, Type)>,
|
||||||
|
signature_operates_on_cell_paths: bool,
|
||||||
|
signature_vectorizes_over_list: bool,
|
||||||
|
) -> HashSet<(Type, Type)> {
|
||||||
|
let mut witnessed_type_transformations = HashSet::<(Type, Type)>::new();
|
||||||
|
|
||||||
|
// Skip tests that don't have results to compare to
|
||||||
|
if let Some(example_output) = example.result.as_ref() {
|
||||||
|
if let Some(example_input_type) =
|
||||||
|
eval_pipeline_without_terminal_expression(example.example, cwd, engine_state)
|
||||||
|
{
|
||||||
|
let example_input_type = example_input_type.get_type();
|
||||||
|
let example_output_type = example_output.get_type();
|
||||||
|
|
||||||
|
let example_matches_signature =
|
||||||
|
signature_input_output_types
|
||||||
|
.iter()
|
||||||
|
.any(|(sig_in_type, sig_out_type)| {
|
||||||
|
example_input_type.is_subtype(sig_in_type)
|
||||||
|
&& example_output_type.is_subtype(sig_out_type)
|
||||||
|
&& {
|
||||||
|
witnessed_type_transformations
|
||||||
|
.insert((sig_in_type.clone(), sig_out_type.clone()));
|
||||||
|
true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// The example type checks as vectorization over an input list if both:
|
||||||
|
// 1. The command is declared to vectorize over list input.
|
||||||
|
// 2. There exists an entry t -> u in the type map such that the
|
||||||
|
// example_input_type is a subtype of list<t> and the
|
||||||
|
// example_output_type is a subtype of list<u>.
|
||||||
|
let example_matches_signature_via_vectorization_over_list =
|
||||||
|
signature_vectorizes_over_list
|
||||||
|
&& match &example_input_type {
|
||||||
|
Type::List(ex_in_type) => {
|
||||||
|
match signature_input_output_types.iter().find_map(
|
||||||
|
|(sig_in_type, sig_out_type)| {
|
||||||
|
if ex_in_type.is_subtype(sig_in_type) {
|
||||||
|
Some((sig_in_type, sig_out_type))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Some((sig_in_type, sig_out_type)) => match &example_output_type
|
||||||
|
{
|
||||||
|
Type::List(ex_out_type)
|
||||||
|
if ex_out_type.is_subtype(sig_out_type) =>
|
||||||
|
{
|
||||||
|
witnessed_type_transformations.insert((
|
||||||
|
sig_in_type.clone(),
|
||||||
|
sig_out_type.clone(),
|
||||||
|
));
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// The example type checks as a cell path operation if both:
|
||||||
|
// 1. The command is declared to operate on cell paths.
|
||||||
|
// 2. The example_input_type is list or record or table, and the example
|
||||||
|
// output shape is the same as the input shape.
|
||||||
|
let example_matches_signature_via_cell_path_operation =
|
||||||
|
signature_operates_on_cell_paths
|
||||||
|
&& example_input_type.accepts_cell_paths()
|
||||||
|
// TODO: This is too permissive; it should make use of the signature.input_output_types at least.
|
||||||
|
&& example_output_type.to_shape() == example_input_type.to_shape();
|
||||||
|
|
||||||
|
if !(example_matches_signature
|
||||||
|
|| example_matches_signature_via_vectorization_over_list
|
||||||
|
|| example_matches_signature_via_cell_path_operation)
|
||||||
|
{
|
||||||
|
panic!(
|
||||||
|
"The example `{}` demonstrates a transformation of type {:?} -> {:?}. \
|
||||||
|
However, this does not match the declared signature: {:?}.{} \
|
||||||
|
For this command, `vectorizes_over_list` is {} and `operates_on_cell_paths()` is {}.",
|
||||||
|
example.example,
|
||||||
|
example_input_type,
|
||||||
|
example_output_type,
|
||||||
|
signature_input_output_types,
|
||||||
|
if signature_input_output_types.is_empty() { " (Did you forget to declare the input and output types for the command?)" } else { "" },
|
||||||
|
signature_vectorizes_over_list,
|
||||||
|
signature_operates_on_cell_paths
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
witnessed_type_transformations
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_example_evaluates_to_expected_output(
|
||||||
|
example: &Example,
|
||||||
|
cwd: &std::path::Path,
|
||||||
|
engine_state: &mut Box<EngineState>,
|
||||||
|
) {
|
||||||
|
let mut stack = Stack::new();
|
||||||
|
|
||||||
|
// Set up PWD
|
||||||
|
stack.add_env_var("PWD".to_string(), Value::test_string(cwd.to_string_lossy()));
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
.merge_env(&mut stack, cwd)
|
||||||
|
.expect("Error merging environment");
|
||||||
|
|
||||||
|
let empty_input = PipelineData::empty();
|
||||||
|
let result = eval(example.example, empty_input, cwd, engine_state);
|
||||||
|
|
||||||
|
// Note. Value implements PartialEq for Bool, Int, Float, String and Block
|
||||||
|
// If the command you are testing requires to compare another case, then
|
||||||
|
// you need to define its equality in the Value struct
|
||||||
|
if let Some(expected) = example.result.as_ref() {
|
||||||
|
assert_eq!(
|
||||||
|
&result, expected,
|
||||||
|
"The example result differs from the expected value",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_all_signature_input_output_types_entries_have_examples(
|
||||||
|
signature: Signature,
|
||||||
|
witnessed_type_transformations: HashSet<(Type, Type)>,
|
||||||
|
) {
|
||||||
|
let declared_type_transformations =
|
||||||
|
HashSet::from_iter(signature.input_output_types.into_iter());
|
||||||
|
assert!(
|
||||||
|
witnessed_type_transformations.is_subset(&declared_type_transformations),
|
||||||
|
"This should not be possible (bug in test): the type transformations \
|
||||||
|
collected in the course of matching examples to the signature type map \
|
||||||
|
contain type transformations not present in the signature type map."
|
||||||
|
);
|
||||||
|
|
||||||
|
if !signature.allow_variants_without_examples {
|
||||||
|
assert_eq!(
|
||||||
|
witnessed_type_transformations,
|
||||||
|
declared_type_transformations,
|
||||||
|
"There are entries in the signature type map which do not correspond to any example: \
|
||||||
|
{:?}",
|
||||||
|
declared_type_transformations
|
||||||
|
.difference(&witnessed_type_transformations)
|
||||||
|
.map(|(s1, s2)| format!("{s1} -> {s2}"))
|
||||||
|
.join(", ")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval(
|
||||||
|
contents: &str,
|
||||||
|
input: PipelineData,
|
||||||
|
cwd: &std::path::Path,
|
||||||
|
engine_state: &mut Box<EngineState>,
|
||||||
|
) -> Value {
|
||||||
|
let (block, delta) = parse(contents, engine_state);
|
||||||
|
eval_block(block, input, cwd, engine_state, delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(contents: &str, engine_state: &EngineState) -> (Block, StateDelta) {
|
||||||
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
|
let (output, err) =
|
||||||
|
nu_parser::parse(&mut working_set, None, contents.as_bytes(), false, &[]);
|
||||||
|
|
||||||
|
if let Some(err) = err {
|
||||||
|
panic!("test parse error in `{contents}`: {err:?}")
|
||||||
|
}
|
||||||
|
|
||||||
|
(output, working_set.render())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_block(
|
||||||
|
block: Block,
|
||||||
|
input: PipelineData,
|
||||||
|
cwd: &std::path::Path,
|
||||||
|
engine_state: &mut Box<EngineState>,
|
||||||
|
delta: StateDelta,
|
||||||
|
) -> Value {
|
||||||
|
engine_state
|
||||||
|
.merge_delta(delta)
|
||||||
|
.expect("Error merging delta");
|
||||||
|
|
||||||
|
let mut stack = Stack::new();
|
||||||
|
|
||||||
|
stack.add_env_var("PWD".to_string(), Value::test_string(cwd.to_string_lossy()));
|
||||||
|
|
||||||
|
match nu_engine::eval_block(engine_state, &mut stack, &block, input, true, true) {
|
||||||
|
Err(err) => panic!("test eval error in `{}`: {:?}", "TODO", err),
|
||||||
|
Ok(result) => result.into_value(Span::test_data()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_pipeline_without_terminal_expression(
|
||||||
|
src: &str,
|
||||||
|
cwd: &std::path::Path,
|
||||||
|
engine_state: &mut Box<EngineState>,
|
||||||
|
) -> Option<Value> {
|
||||||
|
let (mut block, delta) = parse(src, engine_state);
|
||||||
|
if block.pipelines.len() == 1 {
|
||||||
|
let n_expressions = block.pipelines[0].elements.len();
|
||||||
|
block.pipelines[0].elements.truncate(&n_expressions - 1);
|
||||||
|
|
||||||
|
if !block.pipelines[0].elements.is_empty() {
|
||||||
|
let empty_input = PipelineData::empty();
|
||||||
|
Some(eval_block(block, empty_input, cwd, engine_state, delta))
|
||||||
|
} else {
|
||||||
|
Some(Value::nothing(Span::test_data()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// E.g. multiple semicolon-separated statements
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
crates/nu-cmd-lang/src/lib.rs
Normal file
8
crates/nu-cmd-lang/src/lib.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
mod core_commands;
|
||||||
|
mod default_context;
|
||||||
|
mod example_test;
|
||||||
|
|
||||||
|
pub use core_commands::*;
|
||||||
|
pub use default_context::*;
|
||||||
|
#[cfg(test)]
|
||||||
|
pub use example_test::test_examples;
|
@ -15,6 +15,7 @@ bench = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-ansi-term = "0.46.0"
|
nu-ansi-term = "0.46.0"
|
||||||
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.76.1" }
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.76.1" }
|
nu-color-config = { path = "../nu-color-config", version = "0.76.1" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.76.1" }
|
nu-engine = { path = "../nu-engine", version = "0.76.1" }
|
||||||
nu-explore = { path = "../nu-explore", version = "0.76.1" }
|
nu-explore = { path = "../nu-explore", version = "0.76.1" }
|
||||||
|
@ -8,7 +8,7 @@ use nu_protocol::{
|
|||||||
use super::eager::ToDataFrame;
|
use super::eager::ToDataFrame;
|
||||||
use super::expressions::ExprCol;
|
use super::expressions::ExprCol;
|
||||||
use super::lazy::{LazyCollect, ToLazyFrame};
|
use super::lazy::{LazyCollect, ToLazyFrame};
|
||||||
use crate::Let;
|
use nu_cmd_lang::Let;
|
||||||
|
|
||||||
pub fn test_dataframe(cmds: Vec<Box<dyn Command + 'static>>) {
|
pub fn test_dataframe(cmds: Vec<Box<dyn Command + 'static>>) {
|
||||||
if cmds.is_empty() {
|
if cmds.is_empty() {
|
||||||
|
@ -3,7 +3,7 @@ use nu_protocol::engine::{EngineState, StateWorkingSet};
|
|||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub fn create_default_context() -> EngineState {
|
pub fn create_default_context() -> EngineState {
|
||||||
let mut engine_state = EngineState::new();
|
let mut engine_state = nu_cmd_lang::create_default_context();
|
||||||
|
|
||||||
let delta = {
|
let delta = {
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
@ -26,52 +26,6 @@ pub fn create_default_context() -> EngineState {
|
|||||||
#[cfg(feature = "sqlite")]
|
#[cfg(feature = "sqlite")]
|
||||||
add_database_decls(&mut working_set);
|
add_database_decls(&mut working_set);
|
||||||
|
|
||||||
// Core
|
|
||||||
bind_command! {
|
|
||||||
Alias,
|
|
||||||
Break,
|
|
||||||
Commandline,
|
|
||||||
Const,
|
|
||||||
Continue,
|
|
||||||
Def,
|
|
||||||
DefEnv,
|
|
||||||
Describe,
|
|
||||||
Do,
|
|
||||||
Echo,
|
|
||||||
ErrorMake,
|
|
||||||
ExportAlias,
|
|
||||||
ExportCommand,
|
|
||||||
ExportDef,
|
|
||||||
ExportDefEnv,
|
|
||||||
ExportExtern,
|
|
||||||
ExportUse,
|
|
||||||
Extern,
|
|
||||||
For,
|
|
||||||
Help,
|
|
||||||
HelpAliases,
|
|
||||||
HelpCommands,
|
|
||||||
HelpModules,
|
|
||||||
HelpOperators,
|
|
||||||
Hide,
|
|
||||||
HideEnv,
|
|
||||||
If,
|
|
||||||
Ignore,
|
|
||||||
Overlay,
|
|
||||||
OverlayUse,
|
|
||||||
OverlayList,
|
|
||||||
OverlayNew,
|
|
||||||
OverlayHide,
|
|
||||||
Let,
|
|
||||||
Loop,
|
|
||||||
Module,
|
|
||||||
Mut,
|
|
||||||
Return,
|
|
||||||
Try,
|
|
||||||
Use,
|
|
||||||
Version,
|
|
||||||
While,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Charts
|
// Charts
|
||||||
bind_command! {
|
bind_command! {
|
||||||
Histogram
|
Histogram
|
||||||
@ -498,9 +452,6 @@ pub fn create_default_context() -> EngineState {
|
|||||||
MathEvalDeprecated,
|
MathEvalDeprecated,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "plugin")]
|
|
||||||
bind_command!(Register);
|
|
||||||
|
|
||||||
working_set.render()
|
working_set.render()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,12 +9,13 @@ pub fn test_examples(cmd: impl Command + 'static) {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_examples {
|
mod test_examples {
|
||||||
use super::super::{
|
use super::super::{
|
||||||
Ansi, Date, Echo, Enumerate, Flatten, From, Get, If, Into, IntoString, Let, LetEnv, Math,
|
Ansi, Date, Enumerate, Flatten, From, Get, Into, IntoString, LetEnv, Math, MathEuler,
|
||||||
MathEuler, MathPi, MathRound, ParEach, Path, Random, Sort, SortBy, Split, SplitColumn,
|
MathPi, MathRound, ParEach, Path, Random, Sort, SortBy, Split, SplitColumn, SplitRow, Str,
|
||||||
SplitRow, Str, StrJoin, StrLength, StrReplace, Update, Url, Values, Wrap,
|
StrJoin, StrLength, StrReplace, Update, Url, Values, Wrap,
|
||||||
};
|
};
|
||||||
use crate::{Break, Each, Mut, To};
|
use crate::{Each, To};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use nu_cmd_lang::{Break, Echo, If, Let, Mut};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Block,
|
ast::Block,
|
||||||
engine::{Command, EngineState, Stack, StateDelta, StateWorkingSet},
|
engine::{Command, EngineState, Stack, StateDelta, StateWorkingSet},
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::help::highlight_search_string;
|
use nu_cmd_lang::help::highlight_search_string;
|
||||||
|
|
||||||
use fancy_regex::Regex;
|
use fancy_regex::Regex;
|
||||||
use lscolors::{Color as LsColors_Color, LsColors, Style as LsColors_Style};
|
use lscolors::{Color as LsColors_Color, LsColors, Style as LsColors_Style};
|
||||||
|
@ -2,7 +2,6 @@ mod bits;
|
|||||||
mod bytes;
|
mod bytes;
|
||||||
mod charting;
|
mod charting;
|
||||||
mod conversions;
|
mod conversions;
|
||||||
mod core_commands;
|
|
||||||
mod date;
|
mod date;
|
||||||
mod debug;
|
mod debug;
|
||||||
mod default_context;
|
mod default_context;
|
||||||
@ -33,7 +32,6 @@ pub use bits::*;
|
|||||||
pub use bytes::*;
|
pub use bytes::*;
|
||||||
pub use charting::*;
|
pub use charting::*;
|
||||||
pub use conversions::*;
|
pub use conversions::*;
|
||||||
pub use core_commands::*;
|
|
||||||
pub use date::*;
|
pub use date::*;
|
||||||
pub use debug::*;
|
pub use debug::*;
|
||||||
pub use default_context::*;
|
pub use default_context::*;
|
||||||
|
Loading…
Reference in New Issue
Block a user