hm_plugin_protocol/
manifest.rs1use schemars::JsonSchema as DeriveJsonSchema;
6use serde::{Deserialize, Serialize};
7
8use crate::hook::{HookEventKind, HookPhase};
9
10pub type JsonSchema = serde_json::Value;
13
14pub type ClapJson = serde_json::Value;
18
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
21pub struct PluginManifest {
22 pub api_version: u32,
25 pub name: String,
28 pub version: semver::Version,
29 pub description: String,
30 pub capabilities: Vec<Capability>,
31 pub required_host_fns: Vec<String>,
34 pub config_schema: Option<JsonSchema>,
37 #[serde(default)]
43 pub allowed_hosts: Vec<String>,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
47#[serde(tag = "kind", rename_all = "snake_case")]
48pub enum Capability {
49 Subcommand(SubcommandSpec),
50 StepExecutor(StepExecutorSpec),
51 LifecycleHook(LifecycleHookSpec),
52 OutputFormatter(OutputFormatterSpec),
53}
54
55#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
56pub struct SubcommandSpec {
57 pub verb: String,
60 pub about: String,
61 pub args_schema: ClapJson,
64 pub subcommands: Vec<Self>,
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
68pub struct StepExecutorSpec {
69 pub runner: String,
71 pub default: bool,
74 pub step_schema: Option<JsonSchema>,
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
80pub struct LifecycleHookSpec {
81 pub events: Vec<HookEventKind>,
82 pub phase: HookPhase,
83 pub timeout_ms: u32,
84}
85
86#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
87pub struct OutputFormatterSpec {
88 pub name: String,
90 pub mime: String,
92}
93
94#[cfg(test)]
95#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn capability_tagged_serialization() {
101 let cap = Capability::StepExecutor(StepExecutorSpec {
102 runner: "docker".into(),
103 default: true,
104 step_schema: None,
105 });
106 let s = serde_json::to_string(&cap).unwrap();
107 assert!(s.contains(r#""kind":"step_executor""#), "got: {s}");
108 assert!(s.contains(r#""runner":"docker""#), "got: {s}");
109 }
110}