use std::collections::HashSet;
use crate::symbols::SymbolGraph;
use crate::tokens::estimate_tokens;
use crate::{skeleton, Lang};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Archetype {
SingleFileEdit,
Refactor,
Rename,
Feature,
}
pub struct Fixture {
pub name: &'static str,
pub archetype: Archetype,
pub files: Vec<(Lang, String)>,
pub target: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BudgetReport {
pub radius: u8,
pub est_tokens: usize,
pub kept_symbols: usize,
}
impl Fixture {
pub fn context_at(&self, radius: u8) -> String {
let graph = SymbolGraph::from_sources(self.files.iter().map(|(l, s)| (*l, s.as_str())));
let keep = graph.neighbors_within_by_file(&self.target, radius);
self.files
.iter()
.enumerate()
.map(|(i, (lang, source))| skeleton::skeletonize(source, *lang, &keep[i]))
.collect::<Vec<_>>()
.join("\n")
}
pub fn measure(&self, radius: u8) -> BudgetReport {
let graph = SymbolGraph::from_sources(self.files.iter().map(|(l, s)| (*l, s.as_str())));
let keep = graph.neighbors_within_by_file(&self.target, radius);
let context = self
.files
.iter()
.enumerate()
.map(|(i, (lang, source))| skeleton::skeletonize(source, *lang, &keep[i]))
.collect::<Vec<_>>()
.join("\n");
BudgetReport {
radius,
est_tokens: estimate_tokens(&context),
kept_symbols: keep.iter().map(HashSet::len).sum(),
}
}
pub fn sweep(&self, max_radius: u8) -> Vec<BudgetReport> {
(0..=max_radius).map(|r| self.measure(r)).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn fixture() -> Fixture {
Fixture {
name: "demo",
archetype: Archetype::SingleFileEdit,
files: vec![(
Lang::Rust,
"\
fn a() -> i32 { b() + 1 }
fn b() -> i32 { c() + 1 }
fn c() -> i32 { 1 }
fn unrelated() -> i32 { 99 }
"
.to_string(),
)],
target: "a".to_string(),
}
}
#[test]
fn kept_set_never_shrinks_with_radius() {
let reports = fixture().sweep(3);
for pair in reports.windows(2) {
assert!(
pair[1].kept_symbols >= pair[0].kept_symbols,
"kept set shrank from radius {} ({}) to {} ({})",
pair[0].radius,
pair[0].kept_symbols,
pair[1].radius,
pair[1].kept_symbols,
);
}
}
#[test]
fn radius_is_a_real_lever_for_substantial_bodies() {
let f = Fixture {
name: "big_dep",
archetype: Archetype::SingleFileEdit,
files: vec![(
Lang::Rust,
"\
fn target() -> i32 { big() }
fn big() -> i32 {
let mut total = 0;
for i in 0..100 { total += i * i - 3 * i + 7; }
total
}
"
.to_string(),
)],
target: "target".to_string(),
};
assert!(f.measure(1).est_tokens > f.measure(0).est_tokens);
}
#[test]
fn radius_expands_the_kept_set_along_the_dependency_chain() {
let f = fixture();
assert_eq!(f.measure(0).kept_symbols, 1); assert_eq!(f.measure(1).kept_symbols, 2); assert_eq!(f.measure(2).kept_symbols, 3); assert_eq!(f.measure(3).kept_symbols, 3); }
}