nushell-plugin-builder
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseNushell Plugin Builder
Nushell 插件构建器
Overview
概述
This skill helps create Nushell plugins in Rust. Plugins are standalone executables that extend Nushell with custom commands, data transformations, and integrations.
本技能可帮助你在Rust中创建Nushell插件。插件是独立的可执行文件,可为Nushell扩展自定义命令、数据转换功能以及外部集成能力。
Quick Start
快速开始
1. Create Plugin Project
1. 创建插件项目
bash
cargo new nu_plugin_<name>
cd nu_plugin_<name>
cargo add nu-plugin nu-protocolbash
cargo new nu_plugin_<name>
cd nu_plugin_<name>
cargo add nu-plugin nu-protocol2. Basic Plugin Structure
2. 基础插件结构
rust
use nu_plugin::{EvaluatedCall, MsgPackSerializer, serve_plugin};
use nu_plugin::{EngineInterface, Plugin, PluginCommand, SimplePluginCommand};
use nu_protocol::{LabeledError, Signature, Type, Value};
struct MyPlugin;
impl Plugin for MyPlugin {
fn version(&self) -> String {
env!("CARGO_PKG_VERSION").into()
}
fn commands(&self) -> Vec<Box<dyn PluginCommand<Plugin = Self>>> {
vec![Box::new(MyCommand)]
}
}
struct MyCommand;
impl SimplePluginCommand for MyCommand {
type Plugin = MyPlugin;
fn name(&self) -> &str {
"my-command"
}
fn signature(&self) -> Signature {
Signature::build("my-command")
.input_output_type(Type::String, Type::Int)
}
fn run(
&self,
_plugin: &MyPlugin,
_engine: &EngineInterface,
call: &EvaluatedCall,
input: &Value,
) -> Result<Value, LabeledError> {
match input {
Value::String { val, .. } => {
Ok(Value::int(val.len() as i64, call.head))
}
_ => Err(LabeledError::new("Expected string input")
.with_label("requires string", call.head))
}
}
}
fn main() {
serve_plugin(&MyPlugin, MsgPackSerializer)
}rust
use nu_plugin::{EvaluatedCall, MsgPackSerializer, serve_plugin};
use nu_plugin::{EngineInterface, Plugin, PluginCommand, SimplePluginCommand};
use nu_protocol::{LabeledError, Signature, Type, Value};
struct MyPlugin;
impl Plugin for MyPlugin {
fn version(&self) -> String {
env!("CARGO_PKG_VERSION").into()
}
fn commands(&self) -> Vec<Box<dyn PluginCommand<Plugin = Self>>> {
vec![Box::new(MyCommand)]
}
}
struct MyCommand;
impl SimplePluginCommand for MyCommand {
type Plugin = MyPlugin;
fn name(&self) -> &str {
"my-command"
}
fn signature(&self) -> Signature {
Signature::build("my-command")
.input_output_type(Type::String, Type::Int)
}
fn run(
&self,
_plugin: &MyPlugin,
_engine: &EngineInterface,
call: &EvaluatedCall,
input: &Value,
) -> Result<Value, LabeledError> {
match input {
Value::String { val, .. } => {
Ok(Value::int(val.len() as i64, call.head))
}
_ => Err(LabeledError::new("Expected string input")
.with_label("requires string", call.head))
}
}
}
fn main() {
serve_plugin(&MyPlugin, MsgPackSerializer)
}3. Build and Install
3. 构建与安装
bash
undefinedbash
undefinedBuild
Build
cargo build --release
cargo build --release
Install to cargo bin
Install to cargo bin
cargo install --path . --locked
cargo install --path . --locked
Register with nushell
Register with nushell
plugin add ~/.cargo/bin/nu_plugin_<name> # Add .exe on Windows
plugin use <name>
plugin add ~/.cargo/bin/nu_plugin_<name> # Add .exe on Windows
plugin use <name>
Test
Test
"hello" | my-command
undefined"hello" | my-command
undefinedCommand Types
命令类型
SimplePluginCommand
SimplePluginCommand
For commands that operate on single values:
- Input:
&Value - Output:
Result<Value, LabeledError> - Use for: transformations, simple filters, single value operations
适用于处理单个值的命令:
- 输入:
&Value - 输出:
Result<Value, LabeledError> - 适用场景:转换操作、简单过滤、单值处理
PluginCommand
PluginCommand
For commands that handle streams:
- Input:
PipelineData - Output:
Result<PipelineData, LabeledError> - Use for: streaming transformations, lazy processing, large datasets
See for streaming examples.
references/advanced-features.md适用于处理流数据的命令:
- 输入:
PipelineData - 输出:
Result<PipelineData, LabeledError> - 适用场景:流式转换、惰性处理、大型数据集
如需流式处理示例,请查看。
references/advanced-features.mdDefining Command Signatures
定义命令签名
Input-Output Types
输入输出类型
rust
use nu_protocol::{Signature, Type};
Signature::build("my-command")
.input_output_type(Type::String, Type::Int)Common types: , , , , , ,
StringIntFloatBoolList(Box<Type>)Record(...)Anyrust
use nu_protocol::{Signature, Type};
Signature::build("my-command")
.input_output_type(Type::String, Type::Int)常见类型:、、、、、、
StringIntFloatBoolList(Box<Type>)Record(...)AnyParameters
参数
rust
Signature::build("my-command")
// Named flags
.named("output", SyntaxShape::Filepath, "output file", Some('o'))
.switch("verbose", "enable verbose output", Some('v'))
// Positional arguments
.required("input", SyntaxShape::String, "input value")
.optional("count", SyntaxShape::Int, "repeat count")
.rest("files", SyntaxShape::Filepath, "files to process")rust
Signature::build("my-command")
// 命名标志
.named("output", SyntaxShape::Filepath, "output file", Some('o'))
.switch("verbose", "enable verbose output", Some('v'))
// 位置参数
.required("input", SyntaxShape::String, "input value")
.optional("count", SyntaxShape::Int, "repeat count")
.rest("files", SyntaxShape::Filepath, "files to process")Accessing Arguments
访问参数
rust
fn run(&self, call: &EvaluatedCall, ...) -> Result<Value, LabeledError> {
let output: Option<String> = call.get_flag("output")?;
let verbose: bool = call.has_flag("verbose")?;
let input: String = call.req(0)?; // First positional
let count: Option<i64> = call.opt(1)?; // Second positional
let files: Vec<String> = call.rest(2)?; // Remaining args
}rust
fn run(&self, call: &EvaluatedCall, ...) -> Result<Value, LabeledError> {
let output: Option<String> = call.get_flag("output")?;
let verbose: bool = call.has_flag("verbose")?;
let input: String = call.req(0)?; // 第一个位置参数
let count: Option<i64> = call.opt(1)?; // 第二个位置参数
let files: Vec<String> = call.rest(2)?; // 剩余参数
}Error Handling
错误处理
Always return with span information:
LabeledErrorrust
Err(LabeledError::new("Error message")
.with_label("specific issue", call.head))This shows users exactly where the error occurred in their command.
请始终返回包含范围信息的:
LabeledErrorrust
Err(LabeledError::new("Error message")
.with_label("specific issue", call.head))这样可以向用户准确显示命令中错误发生的位置。
Serialization
序列化
MsgPackSerializer (Recommended)
- Binary format, much faster
- Use for production plugins
JsonSerializer
- Text-based, human-readable
- Useful for debugging
Choose in :
main()rust
serve_plugin(&MyPlugin, MsgPackSerializer) // Production
// serve_plugin(&MyPlugin, JsonSerializer) // DebugMsgPackSerializer(推荐)
- 二进制格式,速度更快
- 适用于生产环境插件
JsonSerializer
- 基于文本的格式,人类可读
- 适用于调试场景
在中选择序列化方式:
main()rust
serve_plugin(&MyPlugin, MsgPackSerializer) // 生产环境
// serve_plugin(&MyPlugin, JsonSerializer) // 调试Common Patterns
常见模式
String Transformation
字符串转换
rust
Value::String { val, .. } => {
Ok(Value::string(val.to_uppercase(), call.head))
}rust
Value::String { val, .. } => {
Ok(Value::string(val.to_uppercase(), call.head))
}List Generation
列表生成
rust
let items = vec![
Value::string("a", call.head),
Value::string("b", call.head),
];
Ok(Value::list(items, call.head))rust
let items = vec![
Value::string("a", call.head),
Value::string("b", call.head),
];
Ok(Value::list(items, call.head))Record (Table Row)
记录(表格行)
rust
use nu_protocol::record;
Ok(Value::record(
record! {
"name" => Value::string("example", call.head),
"size" => Value::int(42, call.head),
},
call.head,
))rust
use nu_protocol::record;
Ok(Value::record(
record! {
"name" => Value::string("example", call.head),
"size" => Value::int(42, call.head),
},
call.head,
))Table (List of Records)
表格(记录列表)
rust
let records = vec![
Value::record(record! { "name" => Value::string("a", span) }, span),
Value::record(record! { "name" => Value::string("b", span) }, span),
];
Ok(Value::list(records, call.head))See for complete working examples including:
references/examples.md- Filtering streams
- HTTP API calls
- File system operations
- Multi-command plugins
rust
let records = vec![
Value::record(record! { "name" => Value::string("a", span) }, span),
Value::record(record! { "name" => Value::string("b", span) }, span),
];
Ok(Value::list(records, call.head))完整的可用示例请查看,包括:
references/examples.md- 流数据过滤
- HTTP API调用
- 文件系统操作
- 多命令插件
Development Workflow
开发工作流
Iterative Development
迭代开发
bash
undefinedbash
undefinedBuild
Build
cargo build
cargo build
Test (debug build)
Test (debug build)
plugin add target/debug/nu_plugin_<name>
plugin use <name>
"test" | my-command
plugin add target/debug/nu_plugin_<name>
plugin use <name>
"test" | my-command
After changes, reload
After changes, reload
plugin rm <name>
plugin add target/debug/nu_plugin_<name>
plugin use <name>
undefinedplugin rm <name>
plugin add target/debug/nu_plugin_<name>
plugin use <name>
undefinedAutomated Testing
自动化测试
toml
[dev-dependencies]
nu-plugin-test-support = "0.109.1"rust
#[cfg(test)]
mod tests {
use nu_plugin_test_support::PluginTest;
#[test]
fn test_command() -> Result<(), nu_protocol::ShellError> {
PluginTest::new("myplugin", MyPlugin.into())?
.test_examples(&MyCommand)
}
}See for debugging techniques and troubleshooting.
references/testing-debugging.mdtoml
[dev-dependencies]
nu-plugin-test-support = "0.109.1"rust
#[cfg(test)]
mod tests {
use nu_plugin_test_support::PluginTest;
#[test]
fn test_command() -> Result<(), nu_protocol::ShellError> {
PluginTest::new("myplugin", MyPlugin.into())?
.test_examples(&MyCommand)
}
}调试技巧与故障排除请查看。
references/testing-debugging.mdAdvanced Features
高级功能
Streaming Data
流式数据处理
For lazy processing of large datasets, use :
PipelineDatarust
impl PluginCommand for MyCommand {
fn run(&self, input: PipelineData, ...) -> Result<PipelineData, LabeledError> {
let filtered = input.into_iter().filter(|v| /* condition */);
Ok(PipelineData::ListStream(ListStream::new(filtered, span, None), None))
}
}如需对大型数据集进行惰性处理,请使用:
PipelineDatarust
impl PluginCommand for MyCommand {
fn run(&self, input: PipelineData, ...) -> Result<PipelineData, LabeledError> {
let filtered = input.into_iter().filter(|v| /* condition */);
Ok(PipelineData::ListStream(ListStream::new(filtered, span, None), None))
}
}Engine Interaction
引擎交互
rust
// Get environment variables
let home = engine.get_env_var("HOME")?;
// Set environment variables (before response)
engine.add_env_var("MY_VAR", Value::string("value", span))?;
// Get plugin config from $env.config.plugins.<name>
let config = engine.get_plugin_config()?;
// Get current directory for path resolution
let cwd = engine.get_current_dir()?;rust
// 获取环境变量
let home = engine.get_env_var("HOME")?;
// 设置环境变量(响应前)
engine.add_env_var("MY_VAR", Value::string("value", span))?;
// 从$env.config.plugins.<name>获取插件配置
let config = engine.get_plugin_config()?;
// 获取当前目录用于路径解析
let cwd = engine.get_current_dir()?;Custom Values
自定义值
Define custom data types that extend beyond Nushell's built-in types. See for complete guide.
references/advanced-features.md定义超出Nushell内置类型的自定义数据类型。完整指南请查看。
references/advanced-features.mdImportant Constraints
重要约束
Stdio Restrictions
- Plugins cannot use stdin/stdout (reserved for protocol)
- Check before attempting stdio access
engine.is_using_stdio()
Path Handling
- Always use paths relative to
engine.get_current_dir() - Never assume current working directory
Version Compatibility
- Match and
nu-pluginversionsnu-protocol - Both should match target Nushell version
标准输入输出限制
- 插件无法使用标准输入/输出(预留用于协议通信)
- 在尝试访问标准输入输出前,请检查
engine.is_using_stdio()
路径处理
- 请始终使用相对于的路径
engine.get_current_dir() - 不要假设当前工作目录
版本兼容性
- 请保持和
nu-plugin的版本一致nu-protocol - 两者版本需与目标Nushell版本匹配
Reference Documentation
参考文档
- - Protocol details, serialization, lifecycle
references/plugin-protocol.md - - Streaming, EngineInterface, custom values
references/advanced-features.md - - Complete working examples and patterns
references/examples.md - - Development workflow, debugging, troubleshooting
references/testing-debugging.md
- - 协议细节、序列化、生命周期
references/plugin-protocol.md - - 流式处理、EngineInterface、自定义值
references/advanced-features.md - - 完整可用示例与模式
references/examples.md - - 开发工作流、调试、故障排除
references/testing-debugging.md
External Resources
外部资源
- Official Plugin Guide
- nu-plugin API Docs
- Plugin Examples Repository
- Awesome Nu - Community plugins
- nushellWith - Nix flake for building reproducible Nushell environments with plugins. Includes 100+ pre-packaged plugins from crates.io with binary caching via Garnix. Useful for testing plugins in isolated environments or building standalone Nu scripts.
- 官方插件指南
- nu-plugin API文档
- 插件示例仓库
- Awesome Nu - 社区插件集合
- nushellWith - 用于构建可复现Nushell环境与插件的Nix flake。包含100+来自crates.io的预打包插件,并通过Garnix提供二进制缓存。适用于在隔离环境中测试插件或构建独立的Nu脚本。
Template Script
模板脚本
Use to scaffold a new plugin with proper structure:
scripts/init_plugin.pybash
python3 scripts/init_plugin.py <plugin-name> [--output-dir <path>]This creates a complete working plugin template ready to customize.
使用快速搭建结构规范的新插件:
scripts/init_plugin.pybash
python3 scripts/init_plugin.py <plugin-name> [--output-dir <path>]该脚本会创建一个可直接自定义的完整可用插件模板。",