Skip to main content

khive_pack_code/
pack.rs

1//! `CodePack` struct, `Pack` impl, self-registration factory, and `PackRuntime` impl.
2
3use std::sync::Arc;
4
5use async_trait::async_trait;
6use serde_json::Value;
7
8use khive_runtime::pack::PackRuntime;
9use khive_runtime::{
10    KhiveRuntime, KindHook, NamespaceToken, NoteKindSpec, RuntimeError, SchemaPlan, VerbRegistry,
11};
12use khive_types::{EdgeEndpointRule, HandlerDef, Pack};
13
14use crate::hook::FindingHook;
15use crate::vocab::{CODE_EDGE_RULES, CODE_HANDLERS, CODE_NOTE_KIND_SPECS};
16
17/// Code ontology pack — additive edge rules over four concept subtypes, the
18/// `finding` audit-observation note kind, and the `code.ingest` verb
19/// (ADR-085 Amendment 2).
20///
21/// See `crates/khive-pack-code/docs/code-ontology.md`.
22pub struct CodePack {
23    pub(crate) runtime: KhiveRuntime,
24}
25
26impl Pack for CodePack {
27    const NAME: &'static str = "code";
28    const NOTE_KINDS: &'static [&'static str] = &["finding"];
29    const ENTITY_KINDS: &'static [&'static str] = &[];
30    const HANDLERS: &'static [HandlerDef] = &CODE_HANDLERS;
31    const EDGE_RULES: &'static [EdgeEndpointRule] = &CODE_EDGE_RULES;
32    const REQUIRES: &'static [&'static str] = &["kg"];
33    const NOTE_KIND_SPECS: &'static [NoteKindSpec] = &CODE_NOTE_KIND_SPECS;
34    const SCHEMA_PLAN: Option<khive_runtime::PackSchemaPlan> = None;
35}
36
37impl CodePack {
38    /// Bind the code vocabulary pack to `runtime`; the pack registers no verbs.
39    pub fn new(runtime: KhiveRuntime) -> Self {
40        Self { runtime }
41    }
42}
43
44struct CodePackFactory;
45
46impl khive_runtime::PackFactory for CodePackFactory {
47    fn name(&self) -> &'static str {
48        "code"
49    }
50
51    fn requires(&self) -> &'static [&'static str] {
52        &["kg"]
53    }
54
55    fn create(&self, runtime: KhiveRuntime) -> Box<dyn khive_runtime::PackRuntime> {
56        Box::new(CodePack::new(runtime))
57    }
58}
59
60inventory::submit! { khive_runtime::PackRegistration(&CodePackFactory) }
61
62#[async_trait]
63impl PackRuntime for CodePack {
64    fn name(&self) -> &str {
65        <CodePack as Pack>::NAME
66    }
67
68    fn note_kinds(&self) -> &'static [&'static str] {
69        <CodePack as Pack>::NOTE_KINDS
70    }
71
72    fn entity_kinds(&self) -> &'static [&'static str] {
73        <CodePack as Pack>::ENTITY_KINDS
74    }
75
76    fn handlers(&self) -> &'static [HandlerDef] {
77        <CodePack as Pack>::HANDLERS
78    }
79
80    fn edge_rules(&self) -> &'static [EdgeEndpointRule] {
81        <CodePack as Pack>::EDGE_RULES
82    }
83
84    fn requires(&self) -> &'static [&'static str] {
85        <CodePack as Pack>::REQUIRES
86    }
87
88    fn note_kind_specs(&self) -> &'static [NoteKindSpec] {
89        <CodePack as Pack>::NOTE_KIND_SPECS
90    }
91
92    fn schema_plan(&self) -> SchemaPlan {
93        SchemaPlan {
94            pack: "code",
95            statements: &[],
96        }
97    }
98
99    fn kind_hook(&self, kind: &str) -> Option<Arc<dyn KindHook>> {
100        match kind {
101            "finding" => Some(Arc::new(FindingHook)),
102            _ => None,
103        }
104    }
105
106    async fn dispatch(
107        &self,
108        verb: &str,
109        params: Value,
110        _registry: &VerbRegistry,
111        _token: &NamespaceToken,
112    ) -> Result<Value, RuntimeError> {
113        match verb {
114            "code.ingest" => self.handle_ingest(params).await,
115            _ => Err(RuntimeError::InvalidInput(format!(
116                "code pack does not handle verb {verb:?}"
117            ))),
118        }
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use khive_types::Pack;
125
126    use super::CodePack;
127
128    #[test]
129    fn code_pack_declares_adr085_metadata() {
130        assert_eq!(<CodePack as Pack>::NAME, "code");
131        assert_eq!(<CodePack as Pack>::NOTE_KINDS, &["finding"]);
132        assert!(<CodePack as Pack>::ENTITY_KINDS.is_empty());
133        assert_eq!(<CodePack as Pack>::HANDLERS.len(), 1);
134        assert_eq!(<CodePack as Pack>::HANDLERS[0].name, "code.ingest");
135        assert_eq!(<CodePack as Pack>::REQUIRES, &["kg"]);
136        assert!(<CodePack as Pack>::SCHEMA_PLAN.is_none());
137    }
138}