1use 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_NOTE_KIND_SPECS};
16
17pub struct CodePack {
20 #[allow(dead_code)]
21 runtime: KhiveRuntime,
22}
23
24impl Pack for CodePack {
25 const NAME: &'static str = "code";
26 const NOTE_KINDS: &'static [&'static str] = &["finding"];
27 const ENTITY_KINDS: &'static [&'static str] = &[];
28 const HANDLERS: &'static [HandlerDef] = &[];
29 const EDGE_RULES: &'static [EdgeEndpointRule] = &CODE_EDGE_RULES;
30 const REQUIRES: &'static [&'static str] = &["kg"];
31 const NOTE_KIND_SPECS: &'static [NoteKindSpec] = &CODE_NOTE_KIND_SPECS;
32 const SCHEMA_PLAN: Option<khive_runtime::PackSchemaPlan> = None;
33}
34
35impl CodePack {
36 pub fn new(runtime: KhiveRuntime) -> Self {
37 Self { runtime }
38 }
39}
40
41struct CodePackFactory;
44
45impl khive_runtime::PackFactory for CodePackFactory {
46 fn name(&self) -> &'static str {
47 "code"
48 }
49
50 fn requires(&self) -> &'static [&'static str] {
51 &["kg"]
52 }
53
54 fn create(&self, runtime: KhiveRuntime) -> Box<dyn khive_runtime::PackRuntime> {
55 Box::new(CodePack::new(runtime))
56 }
57}
58
59inventory::submit! { khive_runtime::PackRegistration(&CodePackFactory) }
60
61#[async_trait]
62impl PackRuntime for CodePack {
63 fn name(&self) -> &str {
64 <CodePack as Pack>::NAME
65 }
66
67 fn note_kinds(&self) -> &'static [&'static str] {
68 <CodePack as Pack>::NOTE_KINDS
69 }
70
71 fn entity_kinds(&self) -> &'static [&'static str] {
72 <CodePack as Pack>::ENTITY_KINDS
73 }
74
75 fn handlers(&self) -> &'static [HandlerDef] {
76 <CodePack as Pack>::HANDLERS
77 }
78
79 fn edge_rules(&self) -> &'static [EdgeEndpointRule] {
80 <CodePack as Pack>::EDGE_RULES
81 }
82
83 fn requires(&self) -> &'static [&'static str] {
84 <CodePack as Pack>::REQUIRES
85 }
86
87 fn note_kind_specs(&self) -> &'static [NoteKindSpec] {
88 <CodePack as Pack>::NOTE_KIND_SPECS
89 }
90
91 fn schema_plan(&self) -> SchemaPlan {
92 SchemaPlan {
93 pack: "code",
94 statements: &[],
95 }
96 }
97
98 fn kind_hook(&self, kind: &str) -> Option<Arc<dyn KindHook>> {
99 match kind {
100 "finding" => Some(Arc::new(FindingHook)),
101 _ => None,
102 }
103 }
104
105 async fn dispatch(
106 &self,
107 verb: &str,
108 _params: Value,
109 _registry: &VerbRegistry,
110 _token: &NamespaceToken,
111 ) -> Result<Value, RuntimeError> {
112 Err(RuntimeError::InvalidInput(format!(
113 "code pack does not handle verb {verb:?}"
114 )))
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use khive_types::Pack;
121
122 use super::CodePack;
123
124 #[test]
125 fn code_pack_declares_adr085_metadata() {
126 assert_eq!(<CodePack as Pack>::NAME, "code");
127 assert_eq!(<CodePack as Pack>::NOTE_KINDS, &["finding"]);
128 assert!(<CodePack as Pack>::ENTITY_KINDS.is_empty());
129 assert!(<CodePack as Pack>::HANDLERS.is_empty());
130 assert_eq!(<CodePack as Pack>::REQUIRES, &["kg"]);
131 assert!(<CodePack as Pack>::SCHEMA_PLAN.is_none());
132 }
133}