use std::path::PathBuf;
use crate::{
generator::preprocess::extractors::language_processors::LanguageProcessorManager,
types::code::CodeInsight,
};
pub fn read_code_source(
language_processor: &LanguageProcessorManager,
project_path: &PathBuf,
file_path: &PathBuf,
) -> String {
let full_path = project_path.join(file_path);
if let Ok(content) = std::fs::read_to_string(&full_path) {
truncate_source_code(language_processor, &full_path, &content, 8_1024)
} else {
format!("无法读取文件: {}", full_path.display())
}
}
fn truncate_source_code(
language_processor: &LanguageProcessorManager,
file_path: &std::path::Path,
content: &str,
max_length: usize,
) -> String {
if content.len() <= max_length {
return content.to_string();
}
let lines: Vec<&str> = content.lines().collect();
let mut result = String::new();
let mut current_length = 0;
let mut important_lines = Vec::new();
let mut other_lines = Vec::new();
for (i, line) in lines.iter().enumerate() {
let trimmed = line.trim();
if language_processor.is_important_line(file_path, trimmed) {
important_lines.push((i, line));
} else {
other_lines.push((i, line));
}
}
for (_, line) in important_lines {
if current_length + line.len() > max_length {
break;
}
result.push_str(line);
result.push('\n');
current_length += line.len() + 1;
}
for (_, line) in other_lines {
if current_length + line.len() > max_length {
break;
}
result.push_str(line);
result.push('\n');
current_length += line.len() + 1;
}
if current_length >= max_length {
result.push_str("\n... (代码已截取) ...\n");
}
result
}
pub fn read_dependency_code_source(
language_processor: &LanguageProcessorManager,
analysis: &CodeInsight,
project_path: &PathBuf,
) -> String {
let mut dependency_code = String::new();
let mut total_length = 0;
const MAX_DEPENDENCY_CODE_LENGTH: usize = 4000;
for dep_info in &analysis.dependencies {
if total_length >= MAX_DEPENDENCY_CODE_LENGTH {
dependency_code.push_str("\n... (更多依赖代码已省略) ...\n");
break;
}
if let Some(dep_path) =
find_dependency_file(language_processor, project_path, &dep_info.name)
{
if let Ok(content) = std::fs::read_to_string(&dep_path) {
let truncated =
truncate_source_code(language_processor, &dep_path, &content, 8_1024);
dependency_code.push_str(&format!(
"\n### 依赖: {} ({})\n```\n{}\n```\n",
dep_info.name,
dep_path.display(),
truncated
));
total_length += truncated.len();
}
}
}
if dependency_code.is_empty() {
"无可用的依赖代码".to_string()
} else {
dependency_code
}
}
fn find_dependency_file(
_language_processor: &LanguageProcessorManager,
project_path: &PathBuf,
dep_name: &str,
) -> Option<std::path::PathBuf> {
let clean_name = dep_name
.trim_start_matches("./")
.trim_start_matches("../")
.trim_start_matches("@/")
.trim_start_matches("/");
let possible_paths = vec![
format!("{}.rs", clean_name),
format!("{}/mod.rs", clean_name),
format!("src/{}.rs", clean_name),
format!("src/{}/mod.rs", clean_name),
format!("{}.js", clean_name),
format!("{}.ts", clean_name),
format!("{}.jsx", clean_name),
format!("{}.tsx", clean_name),
format!("{}.mjs", clean_name),
format!("{}.cjs", clean_name),
format!("{}/index.js", clean_name),
format!("{}/index.ts", clean_name),
format!("{}/index.jsx", clean_name),
format!("{}/index.tsx", clean_name),
format!("src/{}.js", clean_name),
format!("src/{}.ts", clean_name),
format!("src/{}.jsx", clean_name),
format!("src/{}.tsx", clean_name),
format!("src/{}/index.js", clean_name),
format!("src/{}/index.ts", clean_name),
format!("{}.vue", clean_name),
format!("src/components/{}.vue", clean_name),
format!("src/views/{}.vue", clean_name),
format!("src/pages/{}.vue", clean_name),
format!("components/{}.vue", clean_name),
format!("views/{}.vue", clean_name),
format!("pages/{}.vue", clean_name),
format!("{}.svelte", clean_name),
format!("src/components/{}.svelte", clean_name),
format!("src/routes/{}.svelte", clean_name),
format!("src/lib/{}.svelte", clean_name),
format!("components/{}.svelte", clean_name),
format!("routes/{}.svelte", clean_name),
format!("lib/{}.svelte", clean_name),
format!("{}.kt", clean_name),
format!("src/main/kotlin/{}.kt", clean_name),
format!("src/main/java/{}.kt", clean_name),
format!("app/src/main/kotlin/{}.kt", clean_name),
format!("app/src/main/java/{}.kt", clean_name),
format!("{}.py", clean_name),
format!("{}/__init__.py", clean_name),
format!("src/{}.py", clean_name),
format!("src/{}/__init__.py", clean_name),
format!("{}.java", clean_name),
format!("src/main/java/{}.java", clean_name),
format!("app/src/main/java/{}.java", clean_name),
];
for path_str in possible_paths {
let full_path = project_path.join(&path_str);
if full_path.exists() {
return Some(full_path);
}
}
recursive_find_file(project_path, clean_name)
}
fn recursive_find_file(project_path: &PathBuf, file_name: &str) -> Option<std::path::PathBuf> {
use std::fs;
let extensions = vec![
"rs", "py", "js", "ts", "jsx", "tsx", "vue", "svelte", "kt", "java", "mjs", "cjs",
];
fn search_directory(
dir: &PathBuf,
target_name: &str,
extensions: &[&str],
) -> Option<std::path::PathBuf> {
if let Ok(entries) = fs::read_dir(dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_file() {
if let Some(file_name) = path.file_stem() {
if let Some(ext) = path.extension() {
if file_name.to_string_lossy() == target_name
&& extensions.contains(&ext.to_string_lossy().as_ref())
{
return Some(path);
}
}
}
} else if path.is_dir() {
if let Some(dir_name) = path.file_name() {
let dir_name_str = dir_name.to_string_lossy();
if !dir_name_str.starts_with('.')
&& dir_name_str != "node_modules"
&& dir_name_str != "target"
&& dir_name_str != "build"
&& dir_name_str != "dist"
{
if let Some(found) = search_directory(&path, target_name, extensions) {
return Some(found);
}
}
}
}
}
}
None
}
search_directory(project_path, file_name, &extensions)
}