233 lines
5.2 KiB
Markdown
233 lines
5.2 KiB
Markdown
|
|
# 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:
|
||
|
|
|
||
|
|
```text
|
||
|
|
Nushell ←→ MessagePack ←→ Plugin Process
|
||
|
|
↓ ↓
|
||
|
|
Script Native Rust
|
||
|
|
```
|
||
|
|
|
||
|
|
## Creating a Plugin
|
||
|
|
|
||
|
|
### Plugin Template
|
||
|
|
|
||
|
|
Generate plugin scaffold:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Create new plugin
|
||
|
|
cargo new --lib nu_plugin_myfeature
|
||
|
|
cd nu_plugin_myfeature
|
||
|
|
```
|
||
|
|
|
||
|
|
Add dependencies to `Cargo.toml`:
|
||
|
|
|
||
|
|
```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:
|
||
|
|
|
||
|
|
```rust
|
||
|
|
// 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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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
|
||
|
|
|
||
|
|
```rust
|
||
|
|
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:
|
||
|
|
|
||
|
|
```rust
|
||
|
|
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
|
||
|
|
|
||
|
|
```rust
|
||
|
|
#[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
|
||
|
|
|
||
|
|
```nushell
|
||
|
|
# 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
|
||
|
|
|
||
|
|
## Related Documentation
|
||
|
|
|
||
|
|
- [Build System](build-system.md) - Building Rust plugins
|
||
|
|
- [Extension Development](extension-development.md) - Extension basics
|
||
|
|
- [Testing](testing.md) - Testing strategies
|