use super::types::Category;
pub use crate::intent::{
consult_raw, ConsultationResult, FindingContext, IntentKind, IntentScope, RawIntentRow,
ScopeLevel,
};
impl IntentKind {
pub fn proposed_for(category: Category) -> IntentKind {
match category {
Category::FramingInterrogation => IntentKind::FramingChoice,
Category::StructuralPatterns
| Category::ImplicitComparison
| Category::SentenceLengthAnomalies => IntentKind::StructuralEcho,
Category::DramatizationGap
| Category::ImplicationTracing
| Category::TemporalDensity => IntentKind::DeliberateTemporalAmbiguity,
Category::AssumptionSurfacing | Category::TensionDetection => {
IntentKind::DeliberateAmbiguity
}
_ => IntentKind::StylisticChoice,
}
}
}
#[derive(Debug, Clone)]
pub struct IntentEntry {
pub id: String,
pub kind: IntentKind,
pub description: String,
pub scope: IntentScope,
pub coverage: Vec<Category>,
pub scope_level: ScopeLevel,
}
impl IntentEntry {
pub fn matches(&self, category: Category, ctx: &FindingContext) -> bool {
self.coverage.contains(&category) && self.scope.applies_to(ctx)
}
}
#[derive(Debug, Clone, Default)]
pub struct IntentLedger {
pub entries: Vec<IntentEntry>,
}
impl IntentLedger {
pub fn consult(&self, category: Category, ctx: &FindingContext) -> ConsultationResult {
match self.entries.iter().find(|e| e.matches(category, ctx)) {
Some(e) => ConsultationResult::Suppress {
entry_id: e.id.clone(),
note: format!("consistent with declared intent: {}", e.description),
},
None => ConsultationResult::Emit,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn entry(scope: IntentScope, coverage: Vec<Category>) -> IntentEntry {
IntentEntry {
id: "e1".into(),
kind: IntentKind::DeliberateAmbiguity,
description: "Mara's loyalty is intentionally unresolved".into(),
scope,
coverage,
scope_level: ScopeLevel::Project,
}
}
#[test]
fn consult_suppresses_covered_in_scope_else_emits() {
let ledger = IntentLedger {
entries: vec![entry(
IntentScope::Chapter("ch07".into()),
vec![Category::AssumptionSurfacing, Category::TensionDetection],
)],
};
let in_ch07 = FindingContext { chapter_id: Some("ch07".into()), ..Default::default() };
let in_ch08 = FindingContext { chapter_id: Some("ch08".into()), ..Default::default() };
match ledger.consult(Category::AssumptionSurfacing, &in_ch07) {
ConsultationResult::Suppress { entry_id, note } => {
assert_eq!(entry_id, "e1");
assert!(note.contains("declared intent"));
}
other => panic!("expected Suppress, got {other:?}"),
}
assert_eq!(ledger.consult(Category::AssumptionSurfacing, &in_ch08), ConsultationResult::Emit);
assert_eq!(ledger.consult(Category::FramingInterrogation, &in_ch07), ConsultationResult::Emit);
}
}