Rust插件库nu-plugin的使用:为Nushell扩展功能与自定义命令的Rust工具包
Rust插件库nu-plugin的使用:为Nushell扩展功能与自定义命令的Rust工具包
nu-plugin是一个为Nushell提供插件API的Rust库。通过它,你可以为Nushell创建自定义命令和扩展功能。
安装
在项目目录中运行以下Cargo命令:
cargo add nu-plugin
或者在Cargo.toml中添加:
nu-plugin = "0.106.1"
示例Demo
以下是一个完整的示例,展示如何使用nu-plugin创建一个简单的Nushell插件:
use nu_plugin::{serve_plugin, EvaluatedCall, LabeledError, MsgPackSerializer, Plugin};
use nu_protocol::{Signature, Value};
// 定义我们的插件结构体
struct MyPlugin;
impl Plugin for MyPlugin {
// 定义插件的命令签名
fn signature(&self) -> Vec<Signature> {
vec![Signature::build("my_command")
.desc("这是一个示例命令")
.required("name", SyntaxShape::String, "要问候的名字")
.optional("count", SyntaxShape::Int, "问候次数")]
}
// 定义命令的行为
fn run(
&mut self,
name: &str,
call: &EvaluatedCall,
input: &Value,
) -> Result<Value, LabeledError> {
// 获取参数
let name: String = call.req(0)?;
let count: Option<i64> = call.opt(1)?;
let count = count.unwrap_or(1);
// 构造输出
let output = (0..count)
.map(|_| format!("你好,{}!", name))
.collect::<Vec<_>>()
.join("\n");
Ok(Value::String {
val: output,
span: call.head,
})
}
}
// 主函数注册并运行插件
fn main() {
serve_plugin(&mut MyPlugin {}, MsgPackSerializer {})
}
使用方法
- 将上述代码保存为
src/main.rs
- 在Cargo.toml中添加
nu-plugin
依赖 - 构建项目:
cargo build --release
- 在Nushell中注册插件:
register /path/to/plugin
- 使用新命令:
my_command "世界" 3
1 回复
Rust插件库nu-plugin的使用:为Nushell扩展功能与自定义命令的Rust工具包
介绍
nu-plugin
是一个Rust工具包,用于为Nushell(一个现代化的Shell环境)开发自定义插件和命令。它提供了必要的工具和接口,让开发者能够用Rust语言扩展Nushell的功能。
Nushell本身是用Rust编写的,而nu-plugin
库让开发者能够:
- 创建自定义命令
- 处理Nushell的数据类型
- 与Nushell的管道系统集成
- 构建高性能的Shell扩展
使用方法
基本步骤
- 创建一个新的Rust库项目:
cargo new --lib my-nu-plugin
cd my-nu-plugin
- 添加
nu-plugin
依赖到Cargo.toml
:
[dependencies]
nu-plugin = "0.80" # 使用与你的Nushell版本兼容的版本
nu-protocol = "0.80"
创建简单插件示例
下面是一个创建简单"hello"命令的示例:
use nu_plugin::{serve_plugin, EvaluatedCall, LabeledError, MsgPackSerializer, Plugin};
use nu_protocol::{Category, PluginSignature, Signature, Value};
struct HelloPlugin;
impl Plugin for HelloPlugin {
// 定义命令签名
fn signature(&self) -> Vec<PluginSignature> {
vec![PluginSignature::build("hello")
.usage("Say hello") // 命令说明
.category(Category::Experimental)] // 命令分类
}
// 实现命令逻辑
fn run(
&mut self,
name: &str,
call: &EvaluatedCall,
input: &Value,
) -> Result<Value, LabeledError> {
// 检查命令名称
if name != "hello" {
return Err(LabeledError {
label: "Wrong command".into(),
msg: "This plugin only implements the 'hello' command".into(),
span: Some(call.head),
});
}
// 获取--name参数值,默认为"world"
Ok(Value::String {
val: format!("Hello, {}!", call.get_flag::<String>("name")?.unwrap_or("world".into())),
span: call.head,
})
}
}
fn main() {
// 启动插件服务
serve_plugin(&mut HelloPlugin {}, MsgPackSerializer)
}
注册和使用插件
- 构建插件:
cargo build --release
- 在Nushell中注册插件:
register /path/to/target/release/libmy_nu_plugin.so
- 使用新命令:
hello
# 输出: Hello, world!
hello --name "NuShell"
# 输出: Hello, NuShell!
高级功能
处理管道输入
fn run(
&mut self,
name: &str,
call: &EvaluatedCall,
input: &Value,
) -> Result<Value, LabeledError> {
// 匹配输入数据类型
match input {
Value::String { val, .. } => Ok(Value::String {
val: format!("You said: {}", val), // 处理字符串输入
span: call.head,
}),
_ => Err(LabeledError { // 不支持的输入类型
label: "Unsupported input".into(),
msg: "This command only supports string input".into(),
span: Some(call.head),
}),
}
}
配置命令参数
fn signature(&self) -> Vec<PluginSignature> {
vec![PluginSignature::build("greet")
.usage("Greet someone") // 命令用途
.required("name", SyntaxShape::String, "Name to greet") // 必需参数
.optional("title", SyntaxShape::String, "Title of the person") // 可选参数
.named("shout", SyntaxShape::Nothing, "Shout the greeting", Some('s')) // 命名标志
.category(Category::Experimental)] // 命令分类
}
完整示例demo
下面是一个完整的插件示例,实现了带有参数和管道处理的"greet"命令:
use nu_plugin::{serve_plugin, EvaluatedCall, LabeledError, MsgPackSerializer, Plugin};
use nu_protocol::{Category, PluginSignature, Signature, SyntaxShape, Value};
struct GreetPlugin;
impl Plugin for GreetPlugin {
fn signature(&self) -> Vec<PluginSignature> {
vec![PluginSignature::build("greet")
.usage("Greet someone with optional title")
.required("name", SyntaxShape::String, "Person's name")
.optional("title", SyntaxShape::String, "Person's title")
.switch("formal", "Use formal greeting", Some('f'))
.category(Category::Experimental)]
}
fn run(
&mut self,
name: &str,
call: &EvaluatedCall,
input: &Value,
) -> Result<Value, LabeledError> {
// 确保处理的是greet命令
if name != "greet" {
return Err(LabeledError {
label: "Wrong command".into(),
msg: "This plugin only implements the 'greet' command".into(),
span: Some(call.head),
});
}
// 获取参数
let person_name: String = call.req(0)?;
let title: Option<String> = call.opt(1)?;
let is_formal = call.has_flag("formal")?;
// 构建问候语
let greeting = if is_formal {
match title {
Some(t) => format!("Dear {} {},", t, person_name),
None => format!("Dear {},", person_name),
}
} else {
format!("Hi {}!", person_name)
};
// 处理管道输入(如果有)
let response = match input {
Value::String { val, .. } => format!("{} {}", greeting, val),
Value::Nothing { .. } => greeting,
_ => return Err(LabeledError {
label: "Unsupported input".into(),
msg: "This command only supports string input".into(),
span: Some(call.head),
}),
};
Ok(Value::String {
val: response,
span: call.head,
})
}
}
fn main() {
serve_plugin(&mut GreetPlugin {}, MsgPackSerializer)
}
最佳实践
- 保持命令简单明确
- 提供清晰的错误信息
- 正确处理Nushell的数据类型
- 为命令添加适当的文档和使用示例
- 考虑性能,特别是处理大量数据时
发布插件
你可以将你的插件发布到:
- crates.io (作为Rust库)
- Nushell的插件目录
- 直接分享编译后的二进制文件
nu-plugin
为扩展Nushell功能提供了强大而灵活的方式,让你能够用Rust的安全性和性能来增强你的Shell体验。