KeiSeiKit-1.0/_primitives/_rust/kei-model/src/fallback.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

59 lines
1.6 KiB
Rust

//! `chain` — walk `fallback` field until None or cycle.
//!
//! Detects cycles via a visited-set. Unknown ids halt the walk before adding
//! the unknown id to the chain. Returns a `Vec<Model>` in walk order with the
//! primary at index 0.
use anyhow::{anyhow, Result};
use std::collections::HashSet;
use crate::model::Model;
use crate::registry::Registry;
/// Walk the fallback chain starting at `primary`.
///
/// Errors:
/// * Unknown `primary` id (caller maps to exit-2).
/// * Cycle detected (caller maps to exit-3).
pub fn chain(primary: &str, registry: &Registry) -> Result<Vec<Model>> {
let first = registry
.get(primary)
.ok_or_else(|| anyhow!("unknown primary model_id: {primary}"))?;
let mut visited: HashSet<String> = HashSet::new();
let mut acc: Vec<Model> = Vec::new();
push_step(&mut visited, &mut acc, first.clone())?;
walk_remaining(&mut visited, &mut acc, registry)?;
Ok(acc)
}
fn walk_remaining(
visited: &mut HashSet<String>,
acc: &mut Vec<Model>,
registry: &Registry,
) -> Result<()> {
while let Some(next_id) = next_id_from(acc) {
match registry.get(&next_id) {
None => return Ok(()),
Some(m) => push_step(visited, acc, m.clone())?,
}
}
Ok(())
}
fn next_id_from(acc: &[Model]) -> Option<String> {
let last = acc.last()?;
last.fallback_target().map(|s| s.to_string())
}
fn push_step(
visited: &mut HashSet<String>,
acc: &mut Vec<Model>,
m: Model,
) -> Result<()> {
if !visited.insert(m.id.clone()) {
return Err(anyhow!("cycle in fallback chain at {}", m.id));
}
acc.push(m);
Ok(())
}