1use serde::{Deserialize, Serialize};
6use std::path::PathBuf;
7
8#[derive(Debug, Clone, Default, Serialize, Deserialize)]
10pub struct SourceContext {
11 pub paths: Vec<PathBuf>,
13 pub snippets: Vec<SourceSnippet>,
15 pub total_tokens: usize,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct SourceSnippet {
22 pub path: PathBuf,
24 pub lines: Option<(usize, usize)>,
26 pub content: String,
28 pub tokens: usize,
30}
31
32impl SourceContext {
33 pub fn new() -> Self {
35 Self::default()
36 }
37
38 pub fn add_path(&mut self, path: PathBuf) {
40 self.paths.push(path);
41 }
42
43 pub fn add_snippet(&mut self, snippet: SourceSnippet) {
45 self.total_tokens += snippet.tokens;
46 self.snippets.push(snippet);
47 }
48
49 pub fn format_for_prompt(&self) -> String {
51 if self.snippets.is_empty() {
52 return String::new();
53 }
54
55 let mut output = String::new();
56 output.push_str("## Source Context (Genchi Genbutsu)\n\n");
57 output.push_str("The following source material must be referenced in your output:\n\n");
58
59 for snippet in &self.snippets {
60 output.push_str(&format!("### {}\n", snippet.path.display()));
61 if let Some((start, end)) = snippet.lines {
62 output.push_str(&format!("Lines {}-{}:\n", start, end));
63 }
64 output.push_str("```\n");
65 output.push_str(&snippet.content);
66 output.push_str("\n```\n\n");
67 }
68
69 output.push_str("**Requirement**: Quote or reference specific line numbers from the source material above.\n\n");
70 output
71 }
72}