Rust协议处理库nu-protocol的使用,nu-protocol为Nushell提供核心协议支持与数据处理功能

Rust协议处理库nu-protocol的使用

nu-protocol 是 Nushell 生态系统中一个核心的 Rust 库,它为整个 Nushell 提供了基础协议支持和数据处理功能。

功能概述

nu-protocol crate 包含了在整个 Nushell 中使用的结构体和特性的定义。这为我们提供了一种将它们暴露给许多其他 crate 的方式,同时也使这些定义可以相互使用,而不会导致相互递归的依赖关系。

安装

要在你的项目中使用 nu-protocol,可以运行以下 Cargo 命令:

cargo add nu-protocol

或者在 Cargo.toml 中添加:

nu-protocol = "0.106.1"

使用示例

以下是一个使用 nu-protocol 的简单示例,展示了如何定义和使用 Nushell 中的基本数据类型:

use nu_protocol::{Value, ShellError, Span};

// 创建一个简单的 Value 对象
fn create_value_example() -> Value {
    Value::String {
        val: "Hello, Nushell!".to_string(),
        span: Span::unknown(),
    }
}

// 处理 Value 对象
fn process_value(value: Value) -> Result<(), ShellError> {
    match value {
        Value::String { val, span } => {
            println!("Got string value: {}", val);
            Ok(())
        }
        _ => Err(ShellError::type_error(
            "string",
            value.get_type().to_string(),
            value.span()?,
        )),
    }
}

// 主函数
fn main() -> Result<(), ShellError> {
    let value = create_value_example();
    process_value(value)?;
    Ok(())
}

高级示例

下面是一个更完整的示例,展示了如何使用 nu-protocol 处理 Nushell 中的管道数据:

use nu_protocol::{
    engine::{Command, EngineState, Stack},
    Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value,
};

// 定义一个自定义命令
#[derive(Clone)]
pub struct MyCustomCommand;

impl Command for MyCustomCommand {
    fn name(&self) -> &str {
        "my_custom_command"
    }

    fn signature(&self) -> Signature {
        Signature::build("my_custom_command")
            .input_output_types(vec![(Type::Any, Type::Any)])
            .category(Category::Experimental)
    }

    fn usage(&self) -> &str {
        "A custom command that processes input data"
    }

    fn examples(&self) -> Vec<Example> {
        vec![Example {
            description: "Process some input",
            example: "some_input | my_custom_command",
            result: None,
        }]
    }

    fn run(
        &self,
        _engine_state: &EngineState,
        _stack: &mut Stack,
        input: PipelineData,
        _call_span: Span,
    ) -> Result<PipelineData, ShellError> {
        // 处理输入数据
        input.map(
            |value| {
                // 对每个值进行转换
                match value {
                    Value::String { val, span } => Value::String {
                        val: format!("Processed: {}", val),
                        span,
                    },
                    Value::Int { val, span } => Value::Int {
                        val: val * 2,
                        span,
                    },
                    _ => value,
                }
            },
            |error| error,
        )
    }
}

// 使用自定义命令
fn use_custom_command() -> Result<(), ShellError> {
    let engine_state = EngineState::new();
    let mut stack = Stack::new();
    
    // 创建输入数据
    let input = Value::String {
        val: "test input".to_string(),
        span: Span::unknown(),
    }.into_pipeline_data();
    
    // 执行命令
    let cmd = MyCustomCommand;
    let output = cmd.run(&engine_state, &mut stack, input, Span::unknown())?;
    
    // 处理输出
    if let Some(value) = output.into_value(Span::unknown()) {
        println!("Command output: {:?}", value);
    }
    
    Ok(())
}

fn main() -> Result<(), ShellError> {
    use_custom_command()
}

完整示例代码

以下是一个结合了基础功能和自定义命令的完整示例:

use nu_protocol::{
    engine::{Command, EngineState, Stack},
    Category, Example, IntoPipelineData, PipelineData, ShellError, 
    Signature, Span, Type, Value
};

// 自定义命令:字符串处理
#[derive(Clone)]
pub struct StringProcessor;

impl Command for StringProcessor {
    fn name(&self) -> &str {
        "str_process"
    }

    fn signature(&self) -> Signature {
        Signature::build("str_process")
            .input_output_type(Type::String, Type::String)
            .category(Category::Strings)
    }

    fn usage(&self) -> &str {
        "Process input string by adding prefix and suffix"
    }

    fn examples(&self) -> Vec<Example> {
        vec![Example {
            description: "Process a string",
            example: r#""hello" | str_process"#,
            result: Some(Value::test_string("PREFIX:hello:SUFFIX")),
        }]
    }

    fn run(
        &self,
        _engine_state: &EngineState,
        _stack: &mut Stack,
        input: PipelineData,
        _call_span: Span,
    ) -> Result<PipelineData, ShellError> {
        input.map(
            |value| {
                match value {
                    Value::String { val, span } => Value::String {
                        val: format!("PREFIX:{}:SUFFIX", val),
                        span,
                    },
                    _ => value,
                }
            },
            |error| error,
        )
    }
}

// 主应用程序
fn main() -> Result<(), ShellError> {
    // 初始化引擎状态和堆栈
    let engine_state = EngineState::new();
    let mut stack = Stack::new();

    // 创建输入数据
    let input = Value::String {
        val: "test string".to_string(),
        span: Span::unknown(),
    }.into_pipeline_data();

    // 创建并执行命令
    let processor = StringProcessor;
    let output = processor.run(&engine_state, &mut stack, input, Span::unknown())?;

    // 输出结果
    if let Value::String { val, .. } = output.into_value(Span::unknown()) {
        println!("处理后的字符串: {}", val);
    }

    Ok(())
}

这个完整示例展示了:

  1. 如何定义一个完整的自定义命令
  2. 如何处理字符串输入
  3. 如何实现命令的签名和示例
  4. 如何在主程序中使用自定义命令处理数据

1 回复

Rust协议处理库nu-protocol的使用指南

概述

nu-protocol是Nushell生态系统的核心协议处理库,为Nushell提供基础数据类型、协议支持和数据处理功能。它定义了Nushell中使用的核心数据结构、值类型以及与shell交互的基本协议。

主要功能

  1. 提供Nushell的核心数据类型(Value, Span等)
  2. 定义插件协议和序列化格式
  3. 处理管道数据流
  4. 支持错误处理和诊断信息

基本使用方法

添加依赖

首先在Cargo.toml中添加依赖:

[dependencies]
nu-protocol = "0.85"  # 使用最新版本

核心数据类型

nu-protocol定义了Nushell的核心Value类型:

use nu_protocol::{Value, Span};

fn create_value() -> Value {
    // 创建一个简单的整数值
    Value::int(42, Span::unknown())
}

fn create_list() -> Value {
    // 创建一个列表值
    Value::list(
        vec![
            Value::int(1, Span::unknown()),
            Value::string("hello", Span::unknown()),
            Value::bool(true, Span::unknown()),
        ],
        Span::unknown(),
    )
}

处理插件协议

nu-protocol提供了与Nushell插件系统交互的功能:

use nu_protocol::{
    PluginSignature, Plugin, EngineInterface, 
    EvaluatedCall, LabeledError, Value
};

struct MyPlugin;

impl Plugin for MyPlugin {
    fn signature(&self) -> Vec<PluginSignature> {
        vec![PluginSignature::build("my-command")
            .desc("A custom command for Nushell")
            .required("input", SyntaxShape::Any, "input value")
        ]
    }

    fn run(
        &mut self,
        name: &str,
        call: &EvaluatedCall,
        input: &Value,
        engine: &EngineInterface,
    ) -> Result<Value, LabeledError> {
        // 处理命令逻辑
        Ok(input.clone())
    }
}

错误处理

use nu_protocol::{LabeledError, Span};

fn might_fail(input: &str) -> Result<Value, LabeledError> {
    if input.is_empty() {
        Err(LabeledError {
            msg: "Input cannot be empty".into(),
            span: Some(Span::unknown()),
            labels: vec![],
            help: Some("Please provide a non-empty string".into()),
        })
    } else {
        Ok(Value::string(input.to_owned(), Span::unknown()))
    }
}

自定义值类型

use nu_protocol::{CustomValue, ShellError, Span, Value};

#[derive(Debug, Clone)]
struct MyCustomValue {
    data: String,
}

impl CustomValue for MyCustomValue {
    fn type_name(&self) -> String {
        "MyCustomValue".into()
    }

    fn to_base_value(&self, span: Span) -> Result<Value, ShellError> {
        Ok(Value::string(self.data.clone(), span))
    }

    fn as_any(&self) &dyn std::any::Any {
        self
    }
}

高级用法

实现自定义命令

use nu_protocol::{
    engine::{Command, EngineState, Stack},
    Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
};

pub struct MyCommand;

impl Command for MyCommand {
    fn name(&self) -> &str {
        "my-command"
    }

    fn signature(&self) -> Signature {
        Signature::build("my-command")
            .input_output_types(vec![(Type::Any, Type::Any)])
            .required("arg", SyntaxShape::Any, "argument description")
            .category(Category::Custom("my-category".into()))
    }

    fn usage(&self) -> &str {
        "Description of what my-command does"
    }

    fn examples(&self) -> Vec<Example> {
        vec![Example {
            description: "Example usage",
            example: "my-command 42",
            result: Some(Value::test_int(42)),
        }]
    }

    fn run(
        &self,
        engine_state: &EngineState,
        stack: &mut Stack,
        call: &Call,
        input: PipelineData,
    ) -> Result<PipelineData, ShellError> {
        // 命令实现逻辑
        Ok(input)
    }
}

处理管道数据

use nu_protocol::{PipelineData, Value, ShellError};

fn process_pipeline(input: PipelineData) -> Result<PipelineData, ShellError> {
    input.map(move |value| {
        // 对管道中的每个值进行处理
        match value {
            Value::Int { val, span } => Value::int(val * 2, span),
            _ => value,
        }
    })
}

完整示例代码

下面是一个完整的Nushell插件示例,展示了如何使用nu-protocol创建一个自定义命令:

use nu_protocol::{
    ast::Call,
    engine::{Command, EngineState, Stack},
    Category, Example, PipelineData, ShellError, Signature, Span, Type, Value,
};

// 定义一个平方命令
pub struct SquareCommand;

impl Command for SquareCommand {
    fn name(&self) -> &str {
        "square"  // 命令名称
    }

    fn signature(&self) -> Signature {
        Signature::build(self.name())
            .input_output_types(vec![(Type::Int, Type::Int)])  // 输入输出类型
            .category(Category::Math)  // 命令分类
    }

    fn usage(&self) -> &str {
        "计算输入整数的平方"  // 命令描述
    }

    fn examples(&self) -> Vec<Example> {
        vec![
            Example {
                description: "计算5的平方",
                example: "5 | square",
                result: Some(Value::test_int(25)),
            },
        ]
    }

    fn run(
        &self,
        _engine_state: &EngineState,
        _stack: &mut Stack,
        call: &Call,
        input: PipelineData,
    ) -> Result<PipelineData, ShellError> {
        // 处理输入数据
        input.map(
            move |value| match value {
                Value::Int { val, span } => {
                    // 计算平方值
                    Value::int(val * val, span)
                }
                other => Value::error(
                    ShellError::OnlySupportsThisInputType {
                        exp_input_type: "integer".into(),
                        wrong_type: other.get_type().to_string(),
                        dst_span: call.head,
                        src_span: other.span(),
                    },
                    call.head,
                ),
            },
        )
    }
}

总结

nu-protocol是开发Nushell插件和扩展的核心库,提供了与Nushell交互所需的所有基础类型和协议。通过它,开发者可以:

  1. 创建自定义命令和插件
  2. 处理Nushell管道数据
  3. 定义自定义值类型
  4. 与Nushell引擎进行深度集成

对于更复杂的用例,建议参考Nushell官方文档和nu-protocol的源代码。

回到顶部