KeiSeiKit-1.0/_primitives/_rust/kei-registry/src/index_substrate.rs
Parfii-bot 0be354a920 KeiSeiKit-public — clean state
Single-commit clean baseline after security scrub of niche-tells,
project codenames, internal jargon, and contributor-email leaks.

Contents:
- 100 Rust crates (_primitives/_rust/)
- 37 agent manifests (_manifests/) + generated specs (_generated/)
- 67 user-invocable skills (skills/)
- 33 hooks (hooks/)
- Composition blocks (_blocks/)
- Documentation (docs/, README.md)
- TS adapter packages (_ts_packages/)
- Assembler (_assembler/)
- Roles (_roles/)
- Templates (_templates/)
- Forgejo CI (.forgejo/)

Author: Denis Parfionovich <info@greendragon.info>

License: see LICENSE.
2026-05-01 12:09:03 +08:00

129 lines
4.4 KiB
Rust

//! `index-substrate` subcommand — bulk registration of all kit substrate dirs.
//!
//! Constructor Pattern: this cube owns the multi-type substrate walk. It
//! delegates per-type scanning to existing scanner adapters (Primitive,
//! Skill, Hook inside kit/hooks, Atom, plus BlockMd, Capability, Role).
//! Each found artefact is forwarded to `registry::register` with idempotency.
//! Output: per-type count table printed as JSON.
use anyhow::Result;
use rusqlite::Connection;
use serde::Serialize;
use serde_json::json;
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use crate::handlers::Outcome;
use crate::paths::resolve_db;
use crate::scanners::atom::AtomScanner;
use crate::scanners::block_md::BlockMdScanner;
use crate::scanners::capability::CapabilityScanner;
use crate::scanners::hook::HookScanner;
use crate::scanners::primitive::PrimitiveScanner;
use crate::scanners::role::RoleScanner;
use crate::scanners::skill::SkillScanner;
use crate::scanners::{Found, Scanner};
use crate::store::open_db;
/// Per-type counts for `index-substrate` output.
#[derive(Debug, Default, Clone, Serialize)]
pub struct IndexCounts {
pub registered: usize,
pub skipped: usize,
pub superseded: usize,
}
/// Top-level handler for `index-substrate`.
pub fn handle_index_substrate(
kit_root: Option<PathBuf>,
db: Option<PathBuf>,
dry_run: bool,
) -> Result<Outcome> {
let root = resolve_kit_root(kit_root);
let conn = open_db(resolve_db(db))?;
let mut by_type: BTreeMap<String, IndexCounts> = BTreeMap::new();
let mut total = IndexCounts::default();
// Hooks scanner root is kit/hooks for index-substrate (kit-internal hooks).
let hooks_root = root.join("hooks");
scan_and_register(&PrimitiveScanner, &root, &conn, "primitives", dry_run, &mut by_type, &mut total)?;
scan_and_register(&SkillScanner, &root, &conn, "skills", dry_run, &mut by_type, &mut total)?;
scan_and_register(&HookScanner, &hooks_root, &conn, "hooks", dry_run, &mut by_type, &mut total)?;
scan_and_register(&AtomScanner, &root, &conn, "atoms", dry_run, &mut by_type, &mut total)?;
scan_and_register(&BlockMdScanner, &root, &conn, "blocks", dry_run, &mut by_type, &mut total)?;
scan_and_register(&CapabilityScanner, &root, &conn, "capabilities", dry_run, &mut by_type, &mut total)?;
scan_and_register(&RoleScanner, &root, &conn, "roles", dry_run, &mut by_type, &mut total)?;
print_summary(&root, total, by_type, dry_run);
Ok(Outcome::Ok)
}
fn resolve_kit_root(kit_root: Option<PathBuf>) -> PathBuf {
let raw = kit_root
.unwrap_or_else(|| std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")));
raw.canonicalize().unwrap_or(raw)
}
fn scan_and_register<S: Scanner>(
scanner: &S,
scan_root: &Path,
conn: &Connection,
label: &str,
dry_run: bool,
by_type: &mut BTreeMap<String, IndexCounts>,
total: &mut IndexCounts,
) -> Result<()> {
let found = scanner.scan(scan_root)?;
let counts = by_type.entry(label.to_string()).or_default();
for f in &found {
if dry_run {
counts.registered += 1;
total.registered += 1;
} else {
register_one(conn, f, counts, total)?;
}
}
Ok(())
}
fn register_one(
conn: &Connection,
f: &Found,
counts: &mut IndexCounts,
total: &mut IndexCounts,
) -> Result<()> {
let pre_existing = crate::registry::find_by_path(conn, &f.path)?;
let block = crate::registry::register(conn, f.block_type, &f.name, &f.path, &f.body, &f.caps)?;
match pre_existing {
Some(prev) if prev.body_sha == block.body_sha => {
counts.skipped += 1;
total.skipped += 1;
}
Some(_) => {
counts.superseded += 1;
counts.registered += 1;
total.superseded += 1;
total.registered += 1;
}
None => {
counts.registered += 1;
total.registered += 1;
}
}
Ok(())
}
fn print_summary(root: &Path, total: IndexCounts, by_type: BTreeMap<String, IndexCounts>, dry_run: bool) {
let body = json!({
"kit_root": root.display().to_string(),
"dry_run": dry_run,
"total": {
"registered": total.registered,
"skipped": total.skipped,
"superseded": total.superseded,
},
"by_type": by_type,
});
println!("{}", serde_json::to_string_pretty(&body).unwrap_or_default());
}