mod cache;
mod decode;
mod render;
#[cfg(test)]
#[path = "tests.rs"]
mod tests;
pub use decode::decode_tripwires;
pub use render::{matching, section, Match};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TripEffect {
Warn,
Deny,
Throttle,
Other(String),
Undeclared,
}
impl TripEffect {
#[must_use]
pub fn parse(raw: Option<&str>) -> Self {
match raw.map(str::trim) {
Some("warn") => Self::Warn,
Some("deny") => Self::Deny,
Some("throttle") => Self::Throttle,
Some("") | None => Self::Undeclared,
Some(other) => Self::Other(other.to_string()),
}
}
#[must_use]
pub fn as_str(&self) -> &str {
match self {
Self::Warn => "warn",
Self::Deny => "deny",
Self::Throttle => "throttle",
Self::Other(s) => s.as_str(),
Self::Undeclared => "(none declared)",
}
}
#[must_use]
pub fn is_blocking(&self) -> bool {
matches!(self, Self::Deny)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GovernedTripwire {
pub policy: String,
pub name: String,
pub paths: Vec<String>,
pub effect: TripEffect,
pub claim: Option<String>,
pub class: Option<String>,
pub verification_point: Option<String>,
pub backoff_formula: Option<String>,
pub conflicts: Vec<String>,
}
impl GovernedTripwire {
#[must_use]
pub fn is_well_formed(&self) -> bool {
self.conflicts.is_empty()
&& self.effect != TripEffect::Undeclared
&& !(self.effect == TripEffect::Throttle && self.backoff_formula.is_none())
}
#[must_use]
pub fn defect(&self) -> Option<String> {
if !self.conflicts.is_empty() {
return Some(format!(
"conflicting values for {} across its rows — effect not trustworthy",
self.conflicts.join(", ")
));
}
if self.effect == TripEffect::Undeclared {
return Some("declares no aegis:effect".to_string());
}
if self.effect == TripEffect::Throttle && self.backoff_formula.is_none() {
return Some(
"effect \"throttle\" with no aegis:backoffFormula — quipu's placement gate \
refuses this, so it should not exist"
.to_string(),
);
}
None
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Provenance {
Live {
endpoint: String,
},
Cached {
endpoint: String,
age_secs: u64,
refresh_error: Option<String>,
},
}
impl Provenance {
#[must_use]
pub fn note(&self) -> String {
match self {
Self::Live { endpoint } => {
format!("source: quipu {endpoint}, read live for this turn")
}
Self::Cached {
endpoint,
age_secs,
refresh_error: None,
} => format!(
"source: quipu {endpoint}, cached projection {} old (within refresh interval)",
human_age(*age_secs)
),
Self::Cached {
endpoint,
age_secs,
refresh_error: Some(e),
} => format!(
"source: quipu {endpoint}, cached projection {} old — REFRESH FAILED ({e}). \
These boundaries may have changed since; treat them as last-known, not current.",
human_age(*age_secs)
),
}
}
}
#[must_use]
pub fn human_age(secs: u64) -> String {
match secs {
0..=90 => format!("{secs}s"),
91..=5400 => format!("{}m", secs / 60),
5401..=172_800 => format!("{}h", secs / 3600),
_ => format!("{}d", secs / 86_400),
}
}
pub use cache::{load_tripwires, TRIPWIRE_QUERY};
pub async fn with_boundaries(
context_text: String,
config: &crate::config::Config,
paths: Vec<String>,
repo_root: &std::path::Path,
format_mode: &str,
) -> String {
let Some((wires, provenance)) = load_tripwires(config, repo_root).await else {
return context_text;
};
let matches = matching(
&wires,
&paths,
config.server.repo_path_prefix.as_deref(),
repo_root,
);
match section(&matches, &provenance, format_mode) {
Some(s) => format!("{s}{context_text}"),
None => context_text,
}
}