provisioning/docs/src/development/plugin-development.md
2026-01-17 03:58:28 +00:00

5.2 KiB

Plugin Development

Developing Nushell plugins for performance-critical operations in the Provisioning platform.

Plugin Overview

Nushell plugins provide 10-50x performance improvement over HTTP APIs through native Rust implementations.

Available Plugins

Plugin Purpose Performance Gain Language
nu_plugin_auth Authentication and OS keyring 5x faster Rust
nu_plugin_kms KMS encryption operations 10x faster Rust
nu_plugin_orchestrator Orchestrator queries 30x faster Rust

Plugin Architecture

Plugins communicate with Nushell via MessagePack protocol:

Nushell ←→ MessagePack ←→ Plugin Process
  ↓                           ↓
Script                    Native Rust

Creating a Plugin

Plugin Template

Generate plugin scaffold:

# Create new plugin
cargo new --lib nu_plugin_myfeature
cd nu_plugin_myfeature

Add dependencies to Cargo.toml:

[package]
name = "nu_plugin_myfeature"
version = "0.1.0"
edition = "2021"

[dependencies]
nu-plugin = "0.109.0"
nu-protocol = "0.109.0"
serde = {version = "1.0", features = ["derive"]}

Plugin Implementation

Implement plugin interface:

// src/lib.rs
use nu_plugin::{EvaluatedCall, LabeledError, Plugin};
use nu_protocol::{Category, PluginSignature, SyntaxShape, Type, Value};

pub struct MyFeaturePlugin;

impl Plugin for MyFeaturePlugin {
    fn signature(&self) -> Vec<PluginSignature> {
        vec![
            PluginSignature::build("my-feature")
                .usage("Perform my feature operation")
                .required("input", SyntaxShape::String, "input value")
                .input_output_type(Type::String, Type::String)
                .category(Category::Custom("provisioning".into())),
        ]
    }

    fn run(
        &mut self,
        name: &str,
        call: &EvaluatedCall,
        input: &Value,
    ) -> Result<Value, LabeledError> {
        match name {
            "my-feature" => self.my_feature(call, input),
            _ => Err(LabeledError {
                label: "Unknown command".into(),
                msg: format!("Unknown command: {}", name),
                span: None,
            }),
        }
    }
}

impl MyFeaturePlugin {
    fn my_feature(&self, call: &EvaluatedCall, _input: &Value) -> Result<Value, LabeledError> {
        let input: String = call.req(0)?;

        // Perform operation
        let result = perform_operation(&input);

        Ok(Value::string(result, call.head))
    }
}

fn perform_operation(input: &str) -> String {
    // Your implementation here
    format!("Processed: {}", input)
}

// Plugin entry point
fn main() {
    nu_plugin::serve_plugin(&mut MyFeaturePlugin, nu_plugin::MsgPackSerializer {})
}

Building Plugin

# Build release version
cargo build --release

# Install plugin
nu -c 'plugin add target/release/nu_plugin_myfeature'
nu -c 'plugin use myfeature'

# Test plugin
nu -c 'my-feature "test input"'

Plugin Performance Optimization

Benchmarking

use std::time::Instant;

pub fn benchmark_operation() {
    let start = Instant::now();

    // Operation to benchmark
    perform_expensive_operation();

    let duration = start.elapsed();
    eprintln!("Operation took: {:?}", duration);
}

Caching

Implement caching for expensive operations:

use std::collections::HashMap;
use std::sync::{Arc, Mutex};

pub struct CachedPlugin {
    cache: Arc<Mutex<HashMap<String, String>>>,
}

impl CachedPlugin {
    fn get_or_compute(&self, key: &str) -> String {
        let mut cache = self.cache.lock().unwrap();

        if let Some(value) = cache.get(key) {
            return value.clone();
        }

        let value = expensive_computation(key);
        cache.insert(key.to_string(), value.clone());
        value
    }
}

Testing Plugins

Unit Tests

#[cfg(test)]
mod tests {
    use super::*;
    use nu_protocol::{Span, Value};

    #[test]
    fn test_my_feature() {
        let plugin = MyFeaturePlugin;
        let input = Value::string("test", Span::test_data());
        let result = plugin.my_feature(&mock_call(), &input).unwrap();

        assert_eq!(result.as_string().unwrap(), "Processed: test");
    }

    fn mock_call() -> EvaluatedCall {
        // Mock EvaluatedCall for testing
        todo!()
    }
}

Integration Tests

# tests/test_plugin.nu
use std assert

def test_plugin_functionality [] {
    let result = my-feature "test input"
    assert equal $result "Processed: test input"
}

def main [] {
    test_plugin_functionality
    print "Plugin tests passed"
}

Plugin Best Practices

  • Keep plugin logic focused and single-purpose
  • Minimize dependencies to reduce binary size
  • Use async operations for I/O-bound tasks
  • Implement proper error handling
  • Document all plugin commands
  • Version plugins with semantic versioning
  • Provide fallback to HTTP API if plugin unavailable
  • Cache expensive computations
  • Profile and benchmark performance improvements