lowfat_plugin/
manifest.rs1use serde::Deserialize;
2
3#[derive(Debug, Deserialize)]
5pub struct PluginManifest {
6 pub plugin: PluginMeta,
7 #[serde(default)]
8 pub runtime: RuntimeConfig,
9 pub hooks: Option<HooksConfig>,
10 pub pipeline: Option<PipelineConfig>,
11}
12
13#[derive(Debug, Deserialize)]
14pub struct PluginMeta {
15 pub name: String,
16 pub version: Option<String>,
17 pub description: Option<String>,
18 pub author: Option<String>,
19 pub category: Option<String>,
20 pub commands: Vec<String>,
22 pub subcommands: Option<Vec<String>>,
24}
25
26#[derive(Debug, Deserialize)]
27pub struct RuntimeConfig {
28 #[serde(default = "default_entry")]
30 pub entry: String,
31 #[serde(default)]
34 pub requires: std::collections::BTreeMap<String, String>,
35}
36
37fn default_entry() -> String {
38 "filter.sh".to_string()
39}
40
41impl Default for RuntimeConfig {
42 fn default() -> Self {
43 Self {
44 entry: default_entry(),
45 requires: Default::default(),
46 }
47 }
48}
49
50#[derive(Debug, Deserialize)]
51pub struct HooksConfig {
52 pub on_install: Option<String>,
53 pub on_update: Option<String>,
54 pub on_remove: Option<String>,
55}
56
57#[derive(Debug, Deserialize)]
58pub struct PipelineConfig {
59 pub pre: Option<Vec<String>>,
60 pub post: Option<Vec<String>>,
61}
62
63impl PluginManifest {
64 pub fn parse(content: &str) -> anyhow::Result<Self> {
65 Ok(toml::from_str(content)?)
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn parse_minimal_manifest() {
75 let toml = r#"
76[plugin]
77name = "git-compact"
78commands = ["git"]
79
80[runtime]
81entry = "filter.sh"
82"#;
83 let manifest = PluginManifest::parse(toml).unwrap();
84 assert_eq!(manifest.plugin.name, "git-compact");
85 assert_eq!(manifest.plugin.commands, vec!["git"]);
86 assert_eq!(manifest.runtime.entry, "filter.sh");
87 }
88
89 #[test]
90 fn parse_minimal_manifest_no_runtime() {
91 let toml = r#"
92[plugin]
93name = "git-compact"
94commands = ["git"]
95"#;
96 let manifest = PluginManifest::parse(toml).unwrap();
97 assert_eq!(manifest.plugin.name, "git-compact");
98 assert_eq!(manifest.runtime.entry, "filter.sh");
99 }
100
101 #[test]
102 fn parse_full_manifest() {
103 let toml = r#"
104[plugin]
105name = "git-compact"
106version = "1.2.0"
107description = "Compact git output for LLM contexts"
108author = "zdk"
109category = "git"
110commands = ["git"]
111subcommands = ["status", "diff", "log", "show"]
112
113[runtime]
114entry = "filter.sh"
115
116[hooks]
117on_install = "chmod +x filter.sh"
118
119[pipeline]
120pre = ["strip-ansi"]
121post = ["truncate"]
122"#;
123 let manifest = PluginManifest::parse(toml).unwrap();
124 assert_eq!(manifest.plugin.name, "git-compact");
125 assert!(manifest.hooks.is_some());
126 assert!(manifest.pipeline.is_some());
127 }
128}