nushell-plugins/nu_plugin_nickel/adr-001-nickel-cli-wrapper-architecture.md

1 line
7.8 KiB
Markdown
Raw Permalink Normal View History

# ADR-001: Nickel Plugin CLI Wrapper Architecture\n\n## Status\n\n**Accepted** - 2025-12-15\n\n## Context\n\nThe nu_plugin_nickel project provides Nushell integration for Nickel configuration language. The core decision was whether to implement this as:\n\n1. **Pure Rust Implementation** (using `nickel-lang-core` crate directly)\n2. **CLI Wrapper** (using `Command::new("nickel")` to invoke external binary)\n\n### Technical Constraints\n\nNickel is a **configuration language with module system**:\n\n- Import system: `import "path/to/module"`\n- Module resolution with search paths\n- Standard library (`builtins`, stdlib packages)\n- Complex evaluation context\n- Package management system\n\n### User Requirements\n\nConfiguration files often use Nickel's module system for:\n\n- Code organization\n- Reusable configurations\n- Standard library access\n- External module dependencies\n\n## Decision\n\nImplement nu_plugin_nickel as a **CLI Wrapper** that invokes the external `nickel` binary.\n\n### Architecture\n\n```plaintext\nNushell Script\n ↓\nnickel-export (plugin command)\n ↓\nhelpers.rs: run_nickel_command()\n ↓\nstd::process::Command::new("nickel")\n ↓\nNickel CLI (official binary)\n ↓\nModule Resolution (guaranteed correct)\n ↓\nJSON/YAML Output\n ↓\nPlugin: serde_json::Value → nu_protocol::Value\n ↓\nNushell Records/Lists\n```\n\n### Implementation Details\n\n**Core Functions** (`helpers.rs`):\n\n```rust\npub(crate) fn run_nickel_command(\n file: &str,\n format: &str,\n output: Option<&str>,\n) -> Result<String>\n```\n\n**Plugin Commands** (`main.rs`):\n\n1. `nickel-export` - Export/evaluate Nickel files (JSON/YAML)\n2. `nickel-eval` - Evaluate with automatic caching (primary config loader)\n3. `nickel-format` - Format Nickel files\n4. `nickel-validate` - Validate Nickel files/directories\n5. `nickel-cache-status` - Show cache information\n\n**Output Processing**:\n\n- Invokes: `nickel export /file.ncl --format json`\n- Captures: stdout (JSON string)\n- Parses: serde_json::Value\n- Converts: `json_value_to_nu_value()` recursive function\n- Returns: nu_protocol::Value (records/lists, not strings)\n\n**Caching** (non-blocking, graceful degradation):\n\n- SHA256 content-addressed cache\n- Location: `~/.cache/provisioning/config-cache/`\n- Key: SHA256(file_content + format + context)\n- Hit rate: Expected 80-90% in typical workflows\n\n### Type System\n\nCommand signatures declare `Type::Any` output:\n\n```rust\n.input_output_type(Type::Any, Type::Any)\n```\n\nThis allows:\n\n- Plugin returns: nu_protocol::Value::Record\n- Nushell receives: proper record (not string)\n- Cell path access works: `nickel-export json /file.ncl | .config | .servers`\n\n## Rationale\n\n### Why CLI Wrapper Over Pure Rust\n\n| Aspect | Pure Rust (nickel-lang-core) | CLI Wrapper (chosen) |\n|--------|----------------------------|----------------------|\n| **Module resolution** | ❓ Undocumented | ✅ Works automatically |\n| **Import system** | ❌ Unclear how to use | ✅ Built-in |\n| **Standard library** | ❌ Access unclear | ✅ Automatic |\n| **Search paths** | ❓ How to configure? | ✅ CLI handles |\n| **Maintenance** | ❌ Track CLI changes | ✅ No maintenance |\n| **Error handling** | ❌ Different from CLI | ✅ Same as CLI |\n| **Complexity** | 🔴 High (undocumented) | 🟢 Low |\n| **External CLI** | ✅ None needed | ✅ Requires nickel binary |\n\n### Why Not Pure Rust\n\nUsing `nickel-lang-core` directly would require:\n\n1. **Understand module resolution**:\n - How does it find imported modules?\n - What are the search paths?\n - How does it resolve `import "base/package"`?\n\n2. **Access standard library**:\n\n ```rust\n // Where is the stdlib?\n let stdlib_path = find_nickel_stdlib()?;\n // Is it version-dependent?\n // How to verify?\n ```\n\n3. **Handle evaluation context**:\n - Build context configuration\n - Search path management\n - Module caching\n - Dependency resolution\n\n4. **Match CLI behavior exactly**:\n - Error messages\n - Valid