use schemars::JsonSchema as DeriveJsonSchema;
use serde::{Deserialize, Serialize};
use crate::hook::{HookEventKind, HookPhase};
pub type JsonSchema = serde_json::Value;
pub type ClapJson = serde_json::Value;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
pub struct PluginManifest {
pub api_version: u32,
pub name: String,
pub version: semver::Version,
pub description: String,
pub capabilities: Vec<Capability>,
pub required_host_fns: Vec<String>,
pub config_schema: Option<JsonSchema>,
#[serde(default)]
pub allowed_hosts: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum Capability {
Subcommand(SubcommandSpec),
StepExecutor(StepExecutorSpec),
LifecycleHook(LifecycleHookSpec),
OutputFormatter(OutputFormatterSpec),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
pub struct SubcommandSpec {
pub verb: String,
pub about: String,
pub args_schema: ClapJson,
pub subcommands: Vec<Self>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
pub struct StepExecutorSpec {
pub runner: String,
pub default: bool,
pub step_schema: Option<JsonSchema>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
pub struct LifecycleHookSpec {
pub events: Vec<HookEventKind>,
pub phase: HookPhase,
pub timeout_ms: u32,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
pub struct OutputFormatterSpec {
pub name: String,
pub mime: String,
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
mod tests {
use super::*;
#[test]
fn capability_tagged_serialization() {
let cap = Capability::StepExecutor(StepExecutorSpec {
runner: "docker".into(),
default: true,
step_schema: None,
});
let s = serde_json::to_string(&cap).unwrap();
assert!(s.contains(r#""kind":"step_executor""#), "got: {s}");
assert!(s.contains(r#""runner":"docker""#), "got: {s}");
}
}