Skip to main content

khive_pack_git/
pack.rs

1//! `GitPack` 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, PackSchemaPlan, RuntimeError, SchemaPlan,
11    VerbRegistry,
12};
13use khive_types::{EdgeEndpointRule, HandlerDef, Pack};
14
15use crate::hook::{CommitHook, IssueLikeHook};
16use crate::vocab::{GIT_NOTE_KIND_SPECS, GIT_SCHEMA_PLAN_STMTS};
17
18/// Git-lifecycle pack (ADR-088, amended by ADR-088 Amendment 1) — registers
19/// `commit` / `issue` / `pull_request` note kinds populated by the batch
20/// ingester in `src/ingest.rs`, and one agent-facing verb, `git.digest`
21/// (`src/handlers.rs`). Extends the base edge contract with `precedes`
22/// commit→commit (parent→child lineage, ADR-088 Amendment 1 ingest
23/// enrichment) — the only new endpoint rule this pack contributes;
24/// everything else uses the base `annotates` contract.
25pub struct GitPack {
26    runtime: KhiveRuntime,
27}
28
29impl Pack for GitPack {
30    const NAME: &'static str = "git";
31    const NOTE_KINDS: &'static [&'static str] = &["commit", "issue", "pull_request"];
32    const ENTITY_KINDS: &'static [&'static str] = &[];
33    const HANDLERS: &'static [HandlerDef] = &crate::vocab::GIT_HANDLERS;
34    const EDGE_RULES: &'static [EdgeEndpointRule] = &crate::vocab::GIT_EDGE_RULES;
35    const REQUIRES: &'static [&'static str] = &["kg"];
36    const NOTE_KIND_SPECS: &'static [NoteKindSpec] = &GIT_NOTE_KIND_SPECS;
37    const SCHEMA_PLAN: Option<PackSchemaPlan> = Some(PackSchemaPlan {
38        pack: "git",
39        statements: &GIT_SCHEMA_PLAN_STMTS,
40    });
41}
42
43impl GitPack {
44    /// Create a new `GitPack` bound to the given runtime.
45    pub fn new(runtime: KhiveRuntime) -> Self {
46        Self { runtime }
47    }
48
49    /// Accessor for `src/handlers.rs`, which lives in a sibling module and
50    /// so cannot reach the private `runtime` field directly (mirrors
51    /// `khive-pack-gtd`'s identical `GtdPack::runtime()` accessor).
52    pub(crate) fn runtime(&self) -> &KhiveRuntime {
53        &self.runtime
54    }
55}
56
57// -- inventory self-registration --------------------------------------------
58
59struct GitPackFactory;
60
61impl khive_runtime::PackFactory for GitPackFactory {
62    fn name(&self) -> &'static str {
63        "git"
64    }
65
66    fn requires(&self) -> &'static [&'static str] {
67        &["kg"]
68    }
69
70    fn create(&self, runtime: KhiveRuntime) -> Box<dyn khive_runtime::PackRuntime> {
71        Box::new(GitPack::new(runtime))
72    }
73}
74
75inventory::submit! { khive_runtime::PackRegistration(&GitPackFactory) }
76
77#[async_trait]
78impl PackRuntime for GitPack {
79    fn name(&self) -> &str {
80        <GitPack as Pack>::NAME
81    }
82
83    fn note_kinds(&self) -> &'static [&'static str] {
84        <GitPack as Pack>::NOTE_KINDS
85    }
86
87    fn entity_kinds(&self) -> &'static [&'static str] {
88        <GitPack as Pack>::ENTITY_KINDS
89    }
90
91    fn handlers(&self) -> &'static [HandlerDef] {
92        <GitPack as Pack>::HANDLERS
93    }
94
95    fn edge_rules(&self) -> &'static [EdgeEndpointRule] {
96        <GitPack as Pack>::EDGE_RULES
97    }
98
99    fn requires(&self) -> &'static [&'static str] {
100        <GitPack as Pack>::REQUIRES
101    }
102
103    fn note_kind_specs(&self) -> &'static [NoteKindSpec] {
104        <GitPack as Pack>::NOTE_KIND_SPECS
105    }
106
107    fn schema_plan(&self) -> SchemaPlan {
108        SchemaPlan {
109            pack: "git",
110            statements: &GIT_SCHEMA_PLAN_STMTS,
111        }
112    }
113
114    fn kind_hook(&self, kind: &str) -> Option<Arc<dyn KindHook>> {
115        match kind {
116            "commit" => Some(Arc::new(CommitHook)),
117            "issue" => Some(Arc::new(IssueLikeHook { kind: "issue" })),
118            "pull_request" => Some(Arc::new(IssueLikeHook {
119                kind: "pull_request",
120            })),
121            _ => None,
122        }
123    }
124
125    async fn dispatch(
126        &self,
127        verb: &str,
128        params: Value,
129        registry: &VerbRegistry,
130        token: &NamespaceToken,
131    ) -> Result<Value, RuntimeError> {
132        match verb {
133            "git.digest" => self.handle_digest(token, registry, params).await,
134            _ => Err(RuntimeError::InvalidInput(format!(
135                "git pack does not handle verb {verb:?}"
136            ))),
137        }
138    }
139}