use crate::installer::server_client::ServerClient;
use std::path::Path;
use std::fs;
use url::Url;
#[derive(Clone, Debug, Default)]
pub struct MainFileFinder {
server_client: ServerClient,
}
impl MainFileFinder {
pub fn new(server_client: ServerClient) -> Self {
Self { server_client }
}
pub fn find_main_file(&self, repo_name: &str, repo_path: &Path, repo_url: Option<&str>) -> Option<String> {
if let Ok(Some(info)) = self.server_client.get_repository_info(repo_name) {
if let Some(main_file) = info.main_file {
if self.validate_main_file(repo_path, &main_file) {
return Some(main_file);
}
}
}
let common_names = [
"run.py", "app.py", "webui.py", "main.py", "start.py",
"launch.py", "gui.py", "interface.py", "server.py"
];
for file_name in common_names {
if self.validate_main_file(repo_path, file_name) {
return Some(file_name.to_string());
}
}
let mut candidates: Vec<String> = Vec::new();
if let Ok(entries) = fs::read_dir(repo_path) {
for entry in entries.flatten() {
if let Ok(file_type) = entry.file_type() {
if file_type.is_file() {
let name = entry.file_name().to_string_lossy().to_string();
if name.to_lowercase().ends_with(".py")
&& !name.contains("test_")
&& name != "setup.py"
&& !name.contains("__")
&& !name.contains("install") {
candidates.push(name);
}
}
}
}
}
if candidates.len() == 1 {
return candidates.into_iter().next();
}
for candidate in &candidates {
let lower_candidate = candidate.to_lowercase();
if lower_candidate.contains("main")
|| lower_candidate.contains("run")
|| lower_candidate.contains("start")
|| lower_candidate.contains("app") {
return Some(candidate.clone());
}
}
if let Some(url) = repo_url {
if let Ok(parsed_url) = Url::parse(url) {
if let Some(name) = parsed_url.path_segments()
.and_then(|s| s.last())
.map(|s| s.trim_end_matches(".git")) {
let candidate = format!("{}.py", name);
if self.validate_main_file(repo_path, &candidate) {
return Some(candidate);
}
}
}
}
None
}
fn validate_main_file(&self, repo_path: &Path, main_file: &str) -> bool {
repo_path.join(main_file).exists()
}
}