the_code_graph_eval/
adapters.rs1use std::collections::HashMap;
2use std::path::{Path, PathBuf};
3
4use domain::error::{CodeGraphError, Result};
5use domain::model::{Edge, FileNode};
6use domain::ports::FileData;
7use parser::resolver::{ResolveContext, ResolverRegistry};
8use parser::{ParseResult, ParserRegistry};
9use rayon::prelude::*;
10use sha2::{Digest, Sha256};
11
12pub struct EvalFileSystem;
17
18impl domain::ports::FileSystem for EvalFileSystem {
19 fn list_files(&self, root: &Path, extensions: &[&str]) -> Result<Vec<PathBuf>> {
20 let mut builder = ignore::WalkBuilder::new(root);
21 builder.add_custom_ignore_filename(".code-graphignore");
22
23 let files: Vec<PathBuf> = builder
24 .build()
25 .filter_map(|entry| entry.ok())
26 .filter(|entry| entry.file_type().is_some_and(|ft| ft.is_file()))
27 .filter(|entry| {
28 entry
29 .path()
30 .extension()
31 .and_then(|ext| ext.to_str())
32 .is_some_and(|ext| extensions.contains(&ext))
33 })
34 .map(|entry| {
35 entry
36 .path()
37 .strip_prefix(root)
38 .unwrap_or(entry.path())
39 .to_path_buf()
40 })
41 .collect();
42
43 Ok(files)
44 }
45
46 fn read_file(&self, path: &Path) -> Result<String> {
47 std::fs::read_to_string(path).map_err(|e| CodeGraphError::FileSystem {
48 path: path.into(),
49 source: e,
50 })
51 }
52
53 fn file_hash(&self, path: &Path) -> Result<String> {
54 let content = std::fs::read(path).map_err(|e| CodeGraphError::FileSystem {
55 path: path.into(),
56 source: e,
57 })?;
58 let mut hasher = Sha256::new();
59 hasher.update(&content);
60 Ok(format!("{:x}", hasher.finalize()))
61 }
62}
63
64pub struct EvalParseProvider {
69 registry: ParserRegistry,
70}
71
72impl Default for EvalParseProvider {
73 fn default() -> Self {
74 Self::new()
75 }
76}
77
78impl EvalParseProvider {
79 pub fn new() -> Self {
80 Self {
81 registry: ParserRegistry::new(),
82 }
83 }
84
85 fn compute_hash(content: &[u8]) -> String {
86 let mut hasher = Sha256::new();
87 hasher.update(content);
88 format!("{:x}", hasher.finalize())
89 }
90}
91
92impl domain::ports::ParseProvider for EvalParseProvider {
93 fn parse_and_resolve(
94 &self,
95 files: &[(PathBuf, Vec<u8>)],
96 project_root: &Path,
97 ) -> Result<Vec<FileData>> {
98 if files.is_empty() {
99 return Ok(vec![]);
100 }
101
102 let parse_results: Vec<(PathBuf, Vec<u8>, ParseResult, domain::model::Language)> = files
104 .par_iter()
105 .filter_map(|(path, source)| {
106 let parser = self.registry.parser_for_file(path)?;
107 match parser.parse(source, path) {
108 Ok(result) => Some((path.clone(), source.clone(), result, parser.language())),
109 Err(e) => {
110 tracing::warn!("parse failed for {}: {e}", path.display());
111 None
112 }
113 }
114 })
115 .collect();
116
117 let parsed_files: HashMap<PathBuf, ParseResult> = parse_results
119 .iter()
120 .map(|(path, _, result, _)| (path.clone(), result.clone()))
121 .collect();
122
123 let file_tree: Vec<PathBuf> = files.iter().map(|(p, _)| p.clone()).collect();
124
125 let context = ResolveContext {
126 project_root: project_root.to_path_buf(),
127 parsed_files,
128 file_tree,
129 };
130
131 let resolver_registry = ResolverRegistry::new(project_root);
133
134 let file_data: Vec<FileData> = parse_results
135 .par_iter()
136 .map(|(path, source, parse_result, lang)| {
137 let resolved_edges = resolver_registry
138 .resolve_file(path, *lang, parse_result, &context)
139 .unwrap_or_else(|e| {
140 tracing::warn!("resolve failed for {}: {e}", path.display());
141 vec![]
142 });
143
144 let mut all_edges: Vec<Edge> = parse_result.edges.clone();
145 all_edges.extend(resolved_edges);
146
147 let file = FileNode {
148 path: path.clone(),
149 language: *lang,
150 hash: Self::compute_hash(source),
151 };
152
153 FileData {
154 file,
155 symbols: parse_result.symbols.clone(),
156 edges: all_edges,
157 }
158 })
159 .collect();
160
161 Ok(file_data)
162 }
163}
164
165use domain::model::DiffHunk;
170use domain::ports::GitProvider;
171
172pub struct NoOpGitProvider;
173
174impl GitProvider for NoOpGitProvider {
175 fn current_head(&self) -> Result<String> {
176 Ok("eval".into())
177 }
178 fn changed_files(&self, _from: &str, _to: &str) -> Result<Vec<PathBuf>> {
179 Ok(vec![])
180 }
181 fn diff_hunks(&self, _from: &str, _to: Option<&str>) -> Result<Vec<DiffHunk>> {
182 Ok(vec![])
183 }
184 fn modified_files(&self) -> Result<Vec<PathBuf>> {
185 Ok(vec![])
186 }
187}