use crate::domain::{OracleKind, OracleStrength, SymbolId};
use std::collections::BTreeMap;
use std::path::PathBuf;
#[derive(Clone, Debug, Default)]
pub struct RustIndex {
pub files: BTreeMap<PathBuf, FileFacts>,
pub tests: Vec<TestFact>,
pub functions: Vec<FunctionFact>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct FileFacts {
pub path: PathBuf,
pub functions: Vec<FunctionFact>,
pub tests: Vec<TestFact>,
pub calls: Vec<CallFact>,
pub returns: Vec<ReturnFact>,
pub literals: Vec<LiteralFact>,
pub probe_shapes: Vec<ProbeShapeFact>,
pub source: String,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FunctionFact {
pub id: SymbolId,
pub name: String,
pub file: PathBuf,
pub start_line: usize,
pub end_line: usize,
pub body: String,
pub calls: Vec<CallFact>,
pub returns: Vec<ReturnFact>,
pub literals: Vec<LiteralFact>,
pub is_test: bool,
pub attrs: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TestFact {
pub name: String,
pub file: PathBuf,
pub start_line: usize,
pub end_line: usize,
pub body: String,
pub calls: Vec<CallFact>,
pub assertions: Vec<OracleFact>,
pub literals: Vec<LiteralFact>,
pub attrs: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OracleFact {
pub line: usize,
pub text: String,
pub kind: OracleKind,
pub strength: OracleStrength,
pub observed_tokens: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CallFact {
pub line: usize,
pub name: String,
pub text: String,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ReturnFact {
pub line: usize,
pub text: String,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LiteralFact {
pub line: usize,
pub value: String,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ProbeShapeFact {
pub start_line: usize,
pub end_line: usize,
pub start_byte: usize,
pub kind: String,
pub text: String,
}
pub type FunctionSummary = FunctionFact;
pub type TestSummary = TestFact;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rust_index_default_has_empty_fact_sets() {
let index = RustIndex::default();
assert!(index.files.is_empty());
assert!(index.tests.is_empty());
assert!(index.functions.is_empty());
}
#[test]
fn file_facts_default_has_empty_collections() {
let facts = FileFacts::default();
assert!(facts.path.as_os_str().is_empty());
assert!(facts.functions.is_empty());
assert!(facts.tests.is_empty());
assert!(facts.calls.is_empty());
assert!(facts.returns.is_empty());
assert!(facts.literals.is_empty());
assert!(facts.probe_shapes.is_empty());
}
#[test]
fn fact_types_clone_and_compare_equal_for_simple_samples() {
let call = CallFact {
line: 1,
name: "test_fn".to_string(),
text: "test_fn()".to_string(),
};
let call_cloned = call.clone();
assert_eq!(call, call_cloned);
let ret = ReturnFact {
line: 2,
text: "return Ok(())".to_string(),
};
let ret_cloned = ret.clone();
assert_eq!(ret, ret_cloned);
let lit = LiteralFact {
line: 3,
value: "42".to_string(),
};
let lit_cloned = lit.clone();
assert_eq!(lit, lit_cloned);
}
#[test]
fn probe_shape_fact_preserves_span_kind_text_and_start_byte() {
let shape = ProbeShapeFact {
start_line: 10,
end_line: 12,
start_byte: 256,
kind: "predicate".to_string(),
text: "x > 0".to_string(),
};
assert_eq!(shape.start_line, 10);
assert_eq!(shape.end_line, 12);
assert_eq!(shape.start_byte, 256);
assert_eq!(shape.kind, "predicate");
assert_eq!(shape.text, "x > 0");
}
}