use serde::Deserialize;
#[derive(Debug, Deserialize)]
pub struct PluginManifest {
pub plugin: PluginMeta,
pub runtime: RuntimeConfig,
pub input: Option<IoConfig>,
pub result: Option<IoConfig>,
pub hooks: Option<HooksConfig>,
pub pipeline: Option<PipelineConfig>,
}
#[derive(Debug, Deserialize)]
pub struct PluginMeta {
pub name: String,
pub version: Option<String>,
pub description: Option<String>,
pub author: Option<String>,
pub category: Option<String>,
pub commands: Vec<String>,
pub subcommands: Option<Vec<String>>,
}
#[derive(Debug, Deserialize)]
pub struct RuntimeConfig {
#[serde(rename = "type")]
pub runtime_type: RuntimeType,
pub entry: String,
pub command: Option<String>,
pub requires: Option<RequiresConfig>,
}
#[derive(Debug, Deserialize, Clone, Copy, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum RuntimeType {
Wasm,
Binary,
Shell,
Python,
Node,
Deno,
Ruby,
Lua,
Custom,
}
#[derive(Debug, Deserialize)]
pub struct RequiresConfig {
pub bins: Option<Vec<String>>,
pub optional_bins: Option<Vec<String>>,
}
#[derive(Debug, Deserialize)]
pub struct IoConfig {
pub format: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct HooksConfig {
pub on_install: Option<String>,
pub on_update: Option<String>,
pub on_remove: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct PipelineConfig {
pub pre: Option<Vec<String>>,
pub post: Option<Vec<String>>,
}
impl PluginManifest {
pub fn parse(content: &str) -> anyhow::Result<Self> {
Ok(toml::from_str(content)?)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_minimal_manifest() {
let toml = r#"
[plugin]
name = "git-compact"
commands = ["git"]
[runtime]
type = "node"
entry = "filter.js"
"#;
let manifest = PluginManifest::parse(toml).unwrap();
assert_eq!(manifest.plugin.name, "git-compact");
assert_eq!(manifest.plugin.commands, vec!["git"]);
assert_eq!(manifest.runtime.runtime_type, RuntimeType::Node);
assert_eq!(manifest.runtime.entry, "filter.js");
}
#[test]
fn parse_full_manifest() {
let toml = r#"
[plugin]
name = "git-compact"
version = "1.2.0"
description = "Compact git output for LLM contexts"
author = "zdk"
category = "git"
commands = ["git"]
subcommands = ["status", "diff", "log", "show"]
[runtime]
type = "wasm"
entry = "filter.wasm"
[runtime.requires]
bins = ["git"]
optional_bins = ["delta"]
[hooks]
on_install = "cargo build --release"
[pipeline]
pre = ["strip-ansi"]
post = ["truncate"]
"#;
let manifest = PluginManifest::parse(toml).unwrap();
assert_eq!(manifest.plugin.name, "git-compact");
assert_eq!(manifest.runtime.runtime_type, RuntimeType::Wasm);
assert!(manifest.hooks.is_some());
assert!(manifest.pipeline.is_some());
}
}