use serde::{Deserialize, Serialize};
use crate::graph::CodeGraph;
use super::citation::{CitationEngine, GroundedClaim};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MaintainedTruth {
pub claim: GroundedClaim,
pub established_at: u64,
pub status: TruthStatus,
pub invalidation: Option<TruthInvalidation>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum TruthStatus {
Valid,
Stale,
Invalidated,
Deleted,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TruthInvalidation {
pub invalidated_at: u64,
pub change: String,
pub new_truth: Option<String>,
}
pub struct TruthMaintainer<'g> {
citation_engine: CitationEngine<'g>,
truths: Vec<MaintainedTruth>,
}
impl<'g> TruthMaintainer<'g> {
pub fn new(graph: &'g CodeGraph) -> Self {
Self {
citation_engine: CitationEngine::new(graph),
truths: Vec::new(),
}
}
pub fn register_truth(&mut self, claim: &str) -> MaintainedTruth {
let grounded = self.citation_engine.ground_claim(claim);
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let truth = MaintainedTruth {
claim: grounded,
established_at: now,
status: TruthStatus::Valid,
invalidation: None,
};
self.truths.push(truth.clone());
truth
}
pub fn check_truth(&self, claim: &str) -> TruthStatus {
let grounded = self.citation_engine.ground_claim(claim);
if grounded.fully_grounded {
TruthStatus::Valid
} else if grounded.citations.is_empty() {
TruthStatus::Deleted
} else {
TruthStatus::Stale
}
}
pub fn refresh_all(&mut self) -> Vec<MaintainedTruth> {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let mut results = Vec::new();
for truth in &mut self.truths {
let new_status = {
let grounded = self.citation_engine.ground_claim(&truth.claim.claim);
if grounded.fully_grounded {
TruthStatus::Valid
} else if grounded.citations.is_empty() {
TruthStatus::Deleted
} else {
TruthStatus::Stale
}
};
if new_status != TruthStatus::Valid && truth.status == TruthStatus::Valid {
truth.status = new_status;
truth.invalidation = Some(TruthInvalidation {
invalidated_at: now,
change: "Graph changed since truth was established".to_string(),
new_truth: None,
});
}
results.push(truth.clone());
}
results
}
pub fn truth_diff(&self) -> Vec<MaintainedTruth> {
self.truths
.iter()
.filter(|t| t.status != TruthStatus::Valid)
.cloned()
.collect()
}
pub fn truths(&self) -> &[MaintainedTruth] {
&self.truths
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{CodeUnit, CodeUnitType, Language, Span};
use std::path::PathBuf;
fn test_graph() -> CodeGraph {
let mut graph = CodeGraph::with_default_dimension();
graph.add_unit(CodeUnit::new(
CodeUnitType::Function,
Language::Python,
"process_payment".to_string(),
"payments.stripe.process_payment".to_string(),
PathBuf::from("src/payments/stripe.py"),
Span::new(10, 0, 30, 0),
));
graph
}
#[test]
fn register_truth_is_valid() {
let graph = test_graph();
let mut maintainer = TruthMaintainer::new(&graph);
let truth = maintainer.register_truth("process_payment exists");
assert_eq!(truth.status, TruthStatus::Valid);
}
#[test]
fn check_truth_valid() {
let graph = test_graph();
let maintainer = TruthMaintainer::new(&graph);
let status = maintainer.check_truth("process_payment exists in codebase");
assert_eq!(status, TruthStatus::Valid);
}
#[test]
fn check_truth_deleted() {
let graph = test_graph();
let maintainer = TruthMaintainer::new(&graph);
let status = maintainer.check_truth("nonexistent_func does something");
assert_eq!(status, TruthStatus::Deleted);
}
#[test]
fn truth_diff_empty_when_valid() {
let graph = test_graph();
let mut maintainer = TruthMaintainer::new(&graph);
maintainer.register_truth("process_payment exists");
let diff = maintainer.truth_diff();
assert!(diff.is_empty());
}
}