Merge stream_example into example plugin and clean up names (#12234)

# Description

As suggested by @WindSoilder, since plugins can now contain both simple
commands that produce `Value` and commands that produce `PipelineData`
without having to choose one or the other for the whole plugin, this
change merges `stream_example` into `example`.

# User-Facing Changes

All of the example plugins are renamed.

# Tests + Formatting

- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`

# After Submitting

- [ ] Check nushell/nushell.github.io for any docs that match the
command names changed
This commit is contained in:
Devyn Cairns
2024-03-19 10:36:46 -07:00
committed by GitHub
parent 02551c416c
commit a29efe28f7
28 changed files with 197 additions and 305 deletions

View File

@ -0,0 +1,51 @@
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, PluginCommand};
use nu_protocol::{Category, PipelineData, PluginExample, PluginSignature, RawStream, Type, Value};
use crate::Example;
/// `<list<string>> | example collect-external`
pub struct CollectExternal;
impl PluginCommand for CollectExternal {
type Plugin = Example;
fn signature(&self) -> PluginSignature {
PluginSignature::build("example collect-external")
.usage("Example transformer to raw external stream")
.search_terms(vec!["example".into()])
.input_output_types(vec![
(Type::List(Type::String.into()), Type::String),
(Type::List(Type::Binary.into()), Type::Binary),
])
.plugin_examples(vec![PluginExample {
example: "[a b] | example collect-external".into(),
description: "collect strings into one stream".into(),
result: Some(Value::test_string("ab")),
}])
.category(Category::Experimental)
}
fn run(
&self,
_plugin: &Example,
_engine: &EngineInterface,
call: &EvaluatedCall,
input: PipelineData,
) -> Result<PipelineData, LabeledError> {
let stream = input.into_iter().map(|value| {
value
.as_str()
.map(|str| str.as_bytes())
.or_else(|_| value.as_binary())
.map(|bin| bin.to_vec())
});
Ok(PipelineData::ExternalStream {
stdout: Some(RawStream::new(Box::new(stream), None, call.head, None)),
stderr: None,
exit_code: None,
span: call.head,
metadata: None,
trim_end_newline: false,
})
}
}

View File

@ -3,13 +3,13 @@ use nu_protocol::{Category, PluginSignature, Type, Value};
use crate::Example;
pub struct NuExampleConfig;
pub struct Config;
impl SimplePluginCommand for NuExampleConfig {
impl SimplePluginCommand for Config {
type Plugin = Example;
fn signature(&self) -> PluginSignature {
PluginSignature::build("nu-example-config")
PluginSignature::build("example config")
.usage("Show plugin configuration")
.extra_usage("The configuration is set under $env.config.plugins.example")
.category(Category::Experimental)

View File

@ -3,13 +3,13 @@ use nu_protocol::{Category, PluginSignature, Value};
use crate::Example;
pub struct NuExampleDisableGc;
pub struct DisableGc;
impl SimplePluginCommand for NuExampleDisableGc {
impl SimplePluginCommand for DisableGc {
type Plugin = Example;
fn signature(&self) -> PluginSignature {
PluginSignature::build("nu-example-disable-gc")
PluginSignature::build("example disable-gc")
.usage("Disable the plugin garbage collector for `example`")
.extra_usage(
"\

View File

@ -3,13 +3,13 @@ use nu_protocol::{Category, PluginSignature, SyntaxShape, Type, Value};
use crate::Example;
pub struct NuExampleEnv;
pub struct Env;
impl SimplePluginCommand for NuExampleEnv {
impl SimplePluginCommand for Env {
type Plugin = Example;
fn signature(&self) -> PluginSignature {
PluginSignature::build("nu-example-env")
PluginSignature::build("example env")
.usage("Get environment variable(s)")
.extra_usage("Returns all environment variables if no name provided")
.category(Category::Experimental)

View File

@ -0,0 +1,45 @@
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, PluginCommand};
use nu_protocol::{Category, PipelineData, PluginExample, PluginSignature, SyntaxShape, Type};
use crate::Example;
/// `<list> | example for-each { |value| ... }`
pub struct ForEach;
impl PluginCommand for ForEach {
type Plugin = Example;
fn signature(&self) -> PluginSignature {
PluginSignature::build("example for-each")
.usage("Example execution of a closure with a stream")
.extra_usage("Prints each value the closure returns to stderr")
.input_output_type(Type::ListStream, Type::Nothing)
.required(
"closure",
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
"The closure to run for each input value",
)
.plugin_examples(vec![PluginExample {
example: "ls | get name | example for-each { |f| ^file $f }".into(),
description: "example with an external command".into(),
result: None,
}])
.category(Category::Experimental)
}
fn run(
&self,
_plugin: &Example,
engine: &EngineInterface,
call: &EvaluatedCall,
input: PipelineData,
) -> Result<PipelineData, LabeledError> {
let closure = call.req(0)?;
let config = engine.get_config()?;
for value in input {
let result = engine.eval_closure(&closure, vec![value.clone()], Some(value))?;
eprintln!("{}", result.to_expanded_string(", ", &config));
}
Ok(PipelineData::Empty)
}
}

View File

@ -0,0 +1,78 @@
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, PluginCommand};
use nu_protocol::{
Category, IntoInterruptiblePipelineData, PipelineData, PluginExample, PluginSignature,
SyntaxShape, Type, Value,
};
use crate::Example;
/// `example generate <initial> { |previous| {out: ..., next: ...} }`
pub struct Generate;
impl PluginCommand for Generate {
type Plugin = Example;
fn signature(&self) -> PluginSignature {
PluginSignature::build("example generate")
.usage("Example execution of a closure to produce a stream")
.extra_usage("See the builtin `generate` command")
.input_output_type(Type::Nothing, Type::ListStream)
.required(
"initial",
SyntaxShape::Any,
"The initial value to pass to the closure",
)
.required(
"closure",
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
"The closure to run to generate values",
)
.plugin_examples(vec![PluginExample {
example: "example generate 0 { |i| if $i <= 10 { {out: $i, next: ($i + 2)} } }"
.into(),
description: "Generate a sequence of numbers".into(),
result: Some(Value::test_list(
[0, 2, 4, 6, 8, 10]
.into_iter()
.map(Value::test_int)
.collect(),
)),
}])
.category(Category::Experimental)
}
fn run(
&self,
_plugin: &Example,
engine: &EngineInterface,
call: &EvaluatedCall,
_input: PipelineData,
) -> Result<PipelineData, LabeledError> {
let engine = engine.clone();
let call = call.clone();
let initial: Value = call.req(0)?;
let closure = call.req(1)?;
let mut next = (!initial.is_nothing()).then_some(initial);
Ok(std::iter::from_fn(move || {
next.take()
.and_then(|value| {
engine
.eval_closure(&closure, vec![value.clone()], Some(value))
.and_then(|record| {
if record.is_nothing() {
Ok(None)
} else {
let record = record.as_record()?;
next = record.get("next").cloned();
Ok(record.get("out").cloned())
}
})
.transpose()
})
.map(|result| result.unwrap_or_else(|err| Value::error(err, call.head)))
})
.into_pipeline_data(None))
}
}

View File

@ -0,0 +1,41 @@
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
use nu_protocol::{Category, PluginSignature, Value};
use crate::Example;
pub struct Main;
impl SimplePluginCommand for Main {
type Plugin = Example;
fn signature(&self) -> PluginSignature {
PluginSignature::build("example")
.usage("Example commands for Nushell plugins")
.extra_usage(
r#"
The `example` plugin demonstrates usage of the Nushell plugin API.
Several commands provided to test and demonstrate different capabilities of
plugins exposed through the API. None of these commands are intended to be
particularly useful.
"#
.trim(),
)
.search_terms(vec!["example".into()])
.category(Category::Experimental)
}
fn run(
&self,
_plugin: &Self::Plugin,
_engine: &EngineInterface,
call: &EvaluatedCall,
_input: &Value,
) -> Result<Value, LabeledError> {
Err(LabeledError {
label: "No subcommand provided".into(),
msg: "add --help to see a list of subcommands".into(),
span: Some(call.head),
})
}
}

View File

@ -1,13 +1,35 @@
mod nu_example_1;
mod nu_example_2;
mod nu_example_3;
mod nu_example_config;
mod nu_example_disable_gc;
mod nu_example_env;
// `example` command - just suggests to call --help
mod main;
pub use nu_example_1::NuExample1;
pub use nu_example_2::NuExample2;
pub use nu_example_3::NuExample3;
pub use nu_example_config::NuExampleConfig;
pub use nu_example_disable_gc::NuExampleDisableGc;
pub use nu_example_env::NuExampleEnv;
pub use main::Main;
// Basic demos
mod one;
mod three;
mod two;
pub use one::One;
pub use three::Three;
pub use two::Two;
// Engine interface demos
mod config;
mod disable_gc;
mod env;
pub use config::Config;
pub use disable_gc::DisableGc;
pub use env::Env;
// Stream demos
mod collect_external;
mod for_each;
mod generate;
mod seq;
mod sum;
pub use collect_external::CollectExternal;
pub use for_each::ForEach;
pub use generate::Generate;
pub use seq::Seq;
pub use sum::Sum;

View File

@ -3,17 +3,17 @@ use nu_protocol::{Category, PluginExample, PluginSignature, SyntaxShape, Value};
use crate::Example;
pub struct NuExample1;
pub struct One;
impl SimplePluginCommand for NuExample1 {
impl SimplePluginCommand for One {
type Plugin = Example;
fn signature(&self) -> PluginSignature {
// The signature defines the usage of the command inside Nu, and also automatically
// generates its help page.
PluginSignature::build("nu-example-1")
PluginSignature::build("example one")
.usage("PluginSignature test 1 for plugin. Returns Value::Nothing")
.extra_usage("Extra usage for nu-example-1")
.extra_usage("Extra usage for example one")
.search_terms(vec!["example".into()])
.required("a", SyntaxShape::Int, "required integer value")
.required("b", SyntaxShape::String, "required string value")
@ -22,7 +22,7 @@ impl SimplePluginCommand for NuExample1 {
.named("named", SyntaxShape::String, "named string", Some('n'))
.rest("rest", SyntaxShape::String, "rest value string")
.plugin_examples(vec![PluginExample {
example: "nu-example-1 3 bb".into(),
example: "example one 3 bb".into(),
description: "running example with an int value and string value".into(),
result: None,
}])

View File

@ -0,0 +1,47 @@
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, PluginCommand};
use nu_protocol::{
Category, ListStream, PipelineData, PluginExample, PluginSignature, SyntaxShape, Type, Value,
};
use crate::Example;
/// `example seq <first> <last>`
pub struct Seq;
impl PluginCommand for Seq {
type Plugin = Example;
fn signature(&self) -> PluginSignature {
PluginSignature::build("example seq")
.usage("Example stream generator for a list of values")
.search_terms(vec!["example".into()])
.required("first", SyntaxShape::Int, "first number to generate")
.required("last", SyntaxShape::Int, "last number to generate")
.input_output_type(Type::Nothing, Type::List(Type::Int.into()))
.plugin_examples(vec![PluginExample {
example: "example seq 1 3".into(),
description: "generate a sequence from 1 to 3".into(),
result: Some(Value::test_list(vec![
Value::test_int(1),
Value::test_int(2),
Value::test_int(3),
])),
}])
.category(Category::Experimental)
}
fn run(
&self,
_plugin: &Example,
_engine: &EngineInterface,
call: &EvaluatedCall,
_input: PipelineData,
) -> Result<PipelineData, LabeledError> {
let first: i64 = call.req(0)?;
let last: i64 = call.req(1)?;
let span = call.head;
let iter = (first..=last).map(move |number| Value::int(number, span));
let list_stream = ListStream::from_stream(iter, None);
Ok(PipelineData::ListStream(list_stream, None))
}
}

View File

@ -0,0 +1,91 @@
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, PluginCommand};
use nu_protocol::{Category, PipelineData, PluginExample, PluginSignature, Span, Type, Value};
use crate::Example;
/// `<list> | example sum`
pub struct Sum;
impl PluginCommand for Sum {
type Plugin = Example;
fn signature(&self) -> PluginSignature {
PluginSignature::build("example sum")
.usage("Example stream consumer for a list of values")
.search_terms(vec!["example".into()])
.input_output_types(vec![
(Type::List(Type::Int.into()), Type::Int),
(Type::List(Type::Float.into()), Type::Float),
])
.plugin_examples(vec![PluginExample {
example: "seq 1 5 | example sum".into(),
description: "sum values from 1 to 5".into(),
result: Some(Value::test_int(15)),
}])
.category(Category::Experimental)
}
fn run(
&self,
_plugin: &Example,
_engine: &EngineInterface,
call: &EvaluatedCall,
input: PipelineData,
) -> Result<PipelineData, LabeledError> {
let mut acc = IntOrFloat::Int(0);
let span = input.span();
for value in input {
if let Ok(n) = value.as_i64() {
acc.add_i64(n);
} else if let Ok(n) = value.as_f64() {
acc.add_f64(n);
} else {
return Err(LabeledError {
label: "Stream only accepts ints and floats".into(),
msg: format!("found {}", value.get_type()),
span,
});
}
}
Ok(PipelineData::Value(acc.to_value(call.head), None))
}
}
/// Accumulates numbers into either an int or a float. Changes type to float on the first
/// float received.
#[derive(Clone, Copy)]
enum IntOrFloat {
Int(i64),
Float(f64),
}
impl IntOrFloat {
pub(crate) fn add_i64(&mut self, n: i64) {
match self {
IntOrFloat::Int(ref mut v) => {
*v += n;
}
IntOrFloat::Float(ref mut v) => {
*v += n as f64;
}
}
}
pub(crate) fn add_f64(&mut self, n: f64) {
match self {
IntOrFloat::Int(v) => {
*self = IntOrFloat::Float(*v as f64 + n);
}
IntOrFloat::Float(ref mut v) => {
*v += n;
}
}
}
pub(crate) fn to_value(self, span: Span) -> Value {
match self {
IntOrFloat::Int(v) => Value::int(v, span),
IntOrFloat::Float(v) => Value::float(v, span),
}
}
}

View File

@ -3,15 +3,15 @@ use nu_protocol::{Category, PluginSignature, SyntaxShape, Value};
use crate::Example;
pub struct NuExample3;
pub struct Three;
impl SimplePluginCommand for NuExample3 {
impl SimplePluginCommand for Three {
type Plugin = Example;
fn signature(&self) -> PluginSignature {
// The signature defines the usage of the command inside Nu, and also automatically
// generates its help page.
PluginSignature::build("nu-example-3")
PluginSignature::build("example three")
.usage("PluginSignature test 3 for plugin. Returns labeled error")
.required("a", SyntaxShape::Int, "required integer value")
.required("b", SyntaxShape::String, "required string value")

View File

@ -3,15 +3,15 @@ use nu_protocol::{record, Category, PluginSignature, SyntaxShape, Value};
use crate::Example;
pub struct NuExample2;
pub struct Two;
impl SimplePluginCommand for NuExample2 {
impl SimplePluginCommand for Two {
type Plugin = Example;
fn signature(&self) -> PluginSignature {
// The signature defines the usage of the command inside Nu, and also automatically
// generates its help page.
PluginSignature::build("nu-example-2")
PluginSignature::build("example two")
.usage("PluginSignature test 2 for plugin. Returns list of records")
.required("a", SyntaxShape::Int, "required integer value")
.required("b", SyntaxShape::String, "required string value")

View File

@ -13,12 +13,21 @@ impl Plugin for Example {
//
// If it doesn't appear on this list, it won't be added.
vec![
Box::new(NuExample1),
Box::new(NuExample2),
Box::new(NuExample3),
Box::new(NuExampleConfig),
Box::new(NuExampleEnv),
Box::new(NuExampleDisableGc),
Box::new(Main),
// Basic demos
Box::new(One),
Box::new(Two),
Box::new(Three),
// Engine interface demos
Box::new(Config),
Box::new(Env),
Box::new(DisableGc),
// Stream demos
Box::new(CollectExternal),
Box::new(ForEach),
Box::new(Generate),
Box::new(Seq),
Box::new(Sum),
]
}
}