Add nu_plugin_once base and cross-rs config

This commit is contained in:
Chris Daßler 2024-08-03 21:48:04 +02:00
parent 07e7c8c81f
commit bb85b2054c
12 changed files with 381 additions and 13 deletions

10
Cargo.lock generated
View File

@ -3503,6 +3503,16 @@ dependencies = [
"semver",
]
[[package]]
name = "nu_plugin_once"
version = "0.1.0"
dependencies = [
"nu-cmd-lang",
"nu-plugin",
"nu-plugin-test-support",
"nu-protocol",
]
[[package]]
name = "nu_plugin_polars"
version = "0.96.2"

View File

@ -51,6 +51,7 @@ members = [
"crates/nu_plugin_query",
"crates/nu_plugin_custom_values",
"crates/nu_plugin_formats",
"crates/nu_plugin_once",
"crates/nu_plugin_polars",
"crates/nu_plugin_stress_internals",
"crates/nu-std",

View File

@ -1,18 +1,10 @@
# Configuration for cross-rs: https://github.com/cross-rs/cross
# Run cross-rs like this:
# cross build --target aarch64-unknown-linux-gnu --release
# or
# cross build --target aarch64-unknown-linux-musl --release --features=static-link-openssl
[target.aarch64-unknown-linux-gnu]
pre-build = [
"dpkg --add-architecture $CROSS_DEB_ARCH",
"apt-get update && apt-get install --assume-yes libssl-dev:$CROSS_DEB_ARCH clang"
]
# cross build --target i686-unknown-linux-musl --release --features=static-link-openssl
# NOTE: for musl you will need to build with --features=static-link-openssl
[target.aarch64-unknown-linux-musl]
[target.i686-unknown-linux-musl]
pre-build = [
"dpkg --add-architecture $CROSS_DEB_ARCH",
"apt-get update && apt-get install --assume-yes clang"
"dpkg --add-architecture i386",
"apt-get update && apt-get install --assume-yes libssl-dev:i386 clang"
]

View File

@ -0,0 +1,23 @@
[package]
authors = ["Chris Daßler"]
description = "A object oriented shell plugin for Nushell"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu_plugin_example"
edition = "2021"
license = "MIT"
name = "nu_plugin_once"
version = "0.1.0"
[[bin]]
name = "nu_plugin_once"
bench = false
[lib]
bench = false
[dependencies]
nu-plugin = { path = "../nu-plugin", version = "0.96.1" }
nu-protocol = { path = "../nu-protocol", version = "0.96.1", features = ["plugin"] }
[dev-dependencies]
nu-plugin-test-support = { path = "../nu-plugin-test-support", version = "0.96.1" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.96.1" }

View File

@ -0,0 +1,36 @@
# Plugin Once
Crate with a simple example of the Plugin trait that needs to be implemented
in order to create a binary that can be registered into nushell declaration list
## `once config`
This subcommand demonstrates sending configuration from the nushell `$env.config` to a plugin.
To make use of the plugin after building `nushell` run:
```nushell
plugin add target/debug/nu_plugin_once
# or then either restart your current nushell session or run:
plugin use target/debug/nu_plugin_once
```
The configuration for the plugin lives in `$env.config.plugins.once`:
```nushell
$env.config = {
plugins: {
once: [
some
values
]
}
}
```
To list plugin values run:
```nushell
once config
```

View File

@ -0,0 +1,47 @@
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
use nu_protocol::{Category, LabeledError, Signature, Value};
use crate::OncePlugin;
pub struct Main;
impl SimplePluginCommand for Main {
type Plugin = OncePlugin;
fn name(&self) -> &str {
"once"
}
fn usage(&self) -> &str {
"once commands for Nushell plugins"
}
fn extra_usage(&self) -> &str {
r#"
The `once` 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()
}
fn signature(&self) -> Signature {
Signature::build(self.name()).category(Category::Experimental)
}
fn search_terms(&self) -> Vec<&str> {
vec!["once"]
}
fn run(
&self,
_plugin: &Self::Plugin,
engine: &EngineInterface,
call: &EvaluatedCall,
_input: &Value,
) -> Result<Value, LabeledError> {
Ok(Value::string(engine.get_help()?, call.head))
}
}

View File

@ -0,0 +1,10 @@
// `once` command - just suggests to call --help
mod main;
pub use main::Main;
mod scenario;
pub use scenario::Scenario;
mod scenario_deploy;
pub use scenario_deploy::ScenarioDeploy;

View File

@ -0,0 +1,76 @@
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
use nu_protocol::{record, Category, Example, LabeledError, Signature, Type, Value};
use crate::OncePlugin;
pub struct Scenario;
impl SimplePluginCommand for Scenario {
type Plugin = OncePlugin;
fn name(&self) -> &str {
"once scenario"
}
fn usage(&self) -> &str {
"Manage once scenarios on local or remote servers."
}
fn extra_usage(&self) -> &str {
"Extra usage for once scenario"
}
fn signature(&self) -> Signature {
Signature::build(self.name())
.category(Category::Experimental)
.input_output_type(Type::Nothing, Type::table())
}
fn search_terms(&self) -> Vec<&str> {
vec!["once", "scenario"]
}
fn examples(&self) -> Vec<Example> {
vec![Example {
example: "once scenario",
description: "List all scenarios",
result: Some(Value::test_list(vec![Value::test_record(record! {
"name" => Value::test_string("test"),
"filename" => Value::test_string("test.scenario.env")
})])),
}]
}
fn run(
&self,
_plugin: &OncePlugin,
_engine: &EngineInterface,
call: &EvaluatedCall,
_input: &Value,
) -> Result<Value, LabeledError> {
let head = call.head;
let list = vec![
Value::record(
record! {
"name" => Value::test_string("dev"),
"filename" => Value::test_string("dev.scenario.env")
},
head,
),
Value::record(
record! {
"name" => Value::test_string("certbot"),
"filename" => Value::test_string("certbot.scenario.env")
},
head,
),
];
Ok(Value::list(list, head))
}
}
#[test]
fn test_examples() -> Result<(), nu_protocol::ShellError> {
use nu_plugin_test_support::PluginTest;
PluginTest::new("once", OncePlugin.into())?.test_command_examples(&Scenario)
}

View File

@ -0,0 +1,65 @@
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
use nu_protocol::{Category, Example, LabeledError, Signature, SyntaxShape, Value};
use crate::OncePlugin;
pub struct ScenarioDeploy;
impl SimplePluginCommand for ScenarioDeploy {
type Plugin = OncePlugin;
fn name(&self) -> &str {
"once scenario.deploy"
}
fn usage(&self) -> &str {
"Deploy once scenarios on local or remote servers."
}
fn extra_usage(&self) -> &str {
"Extra usage for once scenario.deploy"
}
fn signature(&self) -> Signature {
// The signature defines the usage of the command inside Nu, and also automatically
// generates its help page.
Signature::build(self.name())
.required("a", SyntaxShape::Int, "required integer value")
.required("b", SyntaxShape::String, "required string value")
.switch("flag", "a flag for the signature", Some('f'))
.optional("opt", SyntaxShape::Int, "Optional number")
.named("named", SyntaxShape::String, "named string", Some('n'))
.rest("rest", SyntaxShape::String, "rest value string")
.category(Category::Experimental)
}
fn search_terms(&self) -> Vec<&str> {
vec!["once", "scenario", "deploy"]
}
fn examples(&self) -> Vec<Example> {
vec![Example {
example: "once scenario.deploy 3 bb",
description: "running scenario.deploy with an int value and string value",
result: None,
}]
}
fn run(
&self,
plugin: &OncePlugin,
_engine: &EngineInterface,
call: &EvaluatedCall,
input: &Value,
) -> Result<Value, LabeledError> {
plugin.print_values(1, call, input)?;
Ok(Value::nothing(call.head))
}
}
#[test]
fn test_examples() -> Result<(), nu_protocol::ShellError> {
use nu_plugin_test_support::PluginTest;
PluginTest::new("once", OncePlugin.into())?.test_command_examples(&ScenarioDeploy)
}

View File

@ -0,0 +1,25 @@
use nu_plugin::{Plugin, PluginCommand};
mod commands;
mod once;
pub use commands::*;
pub use once::OncePlugin;
impl Plugin for OncePlugin {
fn version(&self) -> String {
env!("CARGO_PKG_VERSION").into()
}
fn commands(&self) -> Vec<Box<dyn PluginCommand<Plugin = Self>>> {
// This is a list of all of the commands you would like Nu to register when your plugin is
// loaded.
//
// If it doesn't appear on this list, it won't be added.
vec![
Box::new(Main),
Box::new(Scenario),
Box::new(ScenarioDeploy)
]
}
}

View File

@ -0,0 +1,30 @@
use nu_plugin::{serve_plugin, MsgPackSerializer};
use nu_plugin_once::OncePlugin;
fn main() {
// When defining your plugin, you can select the Serializer that could be
// used to encode and decode the messages. The available options are
// MsgPackSerializer and JsonSerializer. Both are defined in the serializer
// folder in nu-plugin.
serve_plugin(&OncePlugin {}, MsgPackSerializer {})
// Note
// When creating plugins in other languages one needs to consider how a plugin
// is added and used in nushell.
// The steps are:
// - The plugin is register. In this stage nushell calls the binary file of
// the plugin sending information using the encoded PluginCall::PluginSignature object.
// Use this encoded data in your plugin to design the logic that will return
// the encoded signatures.
// Nushell is expecting and encoded PluginResponse::PluginSignature with all the
// plugin signatures
// - When calling the plugin, nushell sends to the binary file the encoded
// PluginCall::CallInfo which has all the call information, such as the
// values of the arguments, the name of the signature called and the input
// from the pipeline.
// Use this data to design your plugin login and to create the value that
// will be sent to nushell
// Nushell expects an encoded PluginResponse::Value from the plugin
// - If an error needs to be sent back to nushell, one can encode PluginResponse::Error.
// This is a labeled error that nushell can format for pretty printing
}

View File

@ -0,0 +1,53 @@
use nu_plugin::EvaluatedCall;
use nu_protocol::{LabeledError, Value};
pub struct OncePlugin;
impl OncePlugin {
pub fn print_values(
&self,
index: u32,
call: &EvaluatedCall,
input: &Value,
) -> Result<(), LabeledError> {
// Note. When debugging your plugin, you may want to print something to the console
// Use the eprintln macro to print your messages. Trying to print to stdout will
// cause a decoding error for your message
eprintln!("Calling test {index} signature");
eprintln!("value received {input:?}");
// To extract the arguments from the Call object you can use the functions req, has_flag,
// opt, rest, and get_flag
//
// Note that plugin calls only accept simple arguments, this means that you can
// pass to the plug in Int and String. This should be improved when the plugin has
// the ability to call back to NuShell to extract more information
// Keep this in mind when designing your plugin signatures
let a: i64 = call.req(0)?;
let b: String = call.req(1)?;
let flag = call.has_flag("flag")?;
let opt: Option<i64> = call.opt(2)?;
let named: Option<String> = call.get_flag("named")?;
let rest: Vec<String> = call.rest(3)?;
eprintln!("Required values");
eprintln!("a: {a:}");
eprintln!("b: {b:}");
eprintln!("flag: {flag:}");
eprintln!("rest: {rest:?}");
if let Some(v) = opt {
eprintln!("Found optional value opt: {v:}")
} else {
eprintln!("No optional value found")
}
if let Some(v) = named {
eprintln!("Named value: {v:?}")
} else {
eprintln!("No named value found")
}
Ok(())
}
}