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 {})
}

使用方法

  1. 将上述代码保存为src/main.rs
  2. 在Cargo.toml中添加nu-plugin依赖
  3. 构建项目:cargo build --release
  4. 在Nushell中注册插件:register /path/to/plugin
  5. 使用新命令:my_command "世界" 3

1 回复

Rust插件库nu-plugin的使用:为Nushell扩展功能与自定义命令的Rust工具包

介绍

nu-plugin是一个Rust工具包,用于为Nushell(一个现代化的Shell环境)开发自定义插件和命令。它提供了必要的工具和接口,让开发者能够用Rust语言扩展Nushell的功能。

Nushell本身是用Rust编写的,而nu-plugin库让开发者能够:

  • 创建自定义命令
  • 处理Nushell的数据类型
  • 与Nushell的管道系统集成
  • 构建高性能的Shell扩展

使用方法

基本步骤

  1. 创建一个新的Rust库项目:
cargo new --lib my-nu-plugin
cd my-nu-plugin
  1. 添加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)
}

注册和使用插件

  1. 构建插件:
cargo build --release
  1. 在Nushell中注册插件:
register /path/to/target/release/libmy_nu_plugin.so
  1. 使用新命令:
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)
}

最佳实践

  1. 保持命令简单明确
  2. 提供清晰的错误信息
  3. 正确处理Nushell的数据类型
  4. 为命令添加适当的文档和使用示例
  5. 考虑性能,特别是处理大量数据时

发布插件

你可以将你的插件发布到:

  • crates.io (作为Rust库)
  • Nushell的插件目录
  • 直接分享编译后的二进制文件

nu-plugin为扩展Nushell功能提供了强大而灵活的方式,让你能够用Rust的安全性和性能来增强你的Shell体验。

回到顶部