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