use std::{
fs,
fs::File,
io::{self, BufRead, BufReader},
path::{Path, PathBuf},
};
use crate::e_target::{CargoTarget, TargetKind};
use anyhow::{anyhow, Context, Result};
pub fn scan_tests_directory(manifest_path: &Path) -> Result<Vec<String>> {
let project_root = manifest_path
.parent()
.ok_or_else(|| anyhow!("Unable to determine project root from manifest"))?;
let tests_dir = project_root.join("tests");
let mut tests = Vec::new();
if tests_dir.exists() && tests_dir.is_dir() {
for entry in fs::read_dir(tests_dir)? {
let entry = entry?;
let path = entry.path();
if path.is_file() {
if let Some(ext) = path.extension() {
if ext == "rs" {
if let Some(stem) = path.file_stem() {
tests.push(stem.to_string_lossy().to_string());
}
}
}
}
}
}
Ok(tests)
}
pub fn scan_examples_directory(
manifest_path: &Path,
examples_folder: &str,
) -> Result<Vec<CargoTarget>> {
let project_root = manifest_path
.parent()
.ok_or_else(|| anyhow::anyhow!("Unable to determine project root"))?;
let examples_dir = project_root.join(examples_folder);
let mut targets = Vec::new();
if examples_dir.exists() && examples_dir.is_dir() {
for entry in fs::read_dir(&examples_dir)
.with_context(|| format!("Reading directory {:?}", examples_dir))?
{
let entry = entry?;
let path = entry.path();
if path.is_file() {
if let Some(ext) = path.extension() {
if ext == "rs" {
if let Some(stem) = path.file_stem() {
if let Some(target) = CargoTarget::from_source_file(
stem,
&path,
manifest_path,
true,
false,
) {
targets.push(target);
}
}
}
}
} else if path.is_dir() {
if let Some(target) = CargoTarget::from_folder(&path, &manifest_path, true, true) {
if target.kind == TargetKind::Unknown {
continue;
}
targets.push(target);
}
}
}
}
Ok(targets)
}
fn detect_script_kind(path: &Path) -> io::Result<Option<TargetKind>> {
let file = File::open(path)?;
let mut reader = BufReader::new(file);
let mut first_line = String::new();
reader.read_line(&mut first_line)?;
if !first_line.starts_with('#') {
return Ok(None);
}
if first_line.contains("scriptisto") {
return Ok(Some(TargetKind::ScriptScriptisto));
}
if first_line.contains("rust-script") {
return Ok(Some(TargetKind::ScriptRustScript));
}
Ok(None)
}
pub fn determine_target_kind_and_manifest(
manifest_path: &Path,
candidate: &Path,
file_contents: &str,
example: bool,
extended: bool,
_toml_specified: bool,
incoming_kind: Option<TargetKind>,
) -> (TargetKind, PathBuf) {
let mut new_manifest = manifest_path.to_path_buf();
if let Ok(Some(script_kind)) = detect_script_kind(candidate) {
return (script_kind, new_manifest);
}
if let Some(kind) = incoming_kind {
if kind == TargetKind::Test || kind == TargetKind::Bench {
return (kind, new_manifest);
}
}
let tauri_detected = manifest_path
.parent()
.and_then(|p| p.file_name())
.map(|s| s.to_string_lossy().eq_ignore_ascii_case("src-tauri"))
.unwrap_or(false)
|| manifest_path
.parent()
.map(|p| p.join("tauri.conf.json"))
.map_or(false, |p| p.exists())
|| manifest_path
.parent()
.map(|p| p.join("src-tauri"))
.map_or(false, |p| p.exists())
|| candidate
.parent()
.map(|p| p.join("tauri.conf.json"))
.map_or(false, |p| p.exists());
if tauri_detected {
if example {
return (TargetKind::ManifestTauriExample, new_manifest);
}
if let Some(candidate_parent) = candidate.parent() {
let candidate_manifest = candidate_parent.join("Cargo.toml");
if candidate_manifest.exists() {
new_manifest = candidate_manifest;
}
}
return (TargetKind::ManifestTauri, new_manifest);
}
if file_contents.contains("dioxus::") {
let kind = if example {
TargetKind::ManifestDioxusExample
} else {
TargetKind::ManifestDioxus
};
return (kind, new_manifest);
}
if file_contents.contains("leptos::") {
return (TargetKind::ManifestLeptos, new_manifest);
}
if file_contents.contains("fn main") {
let kind = if example {
if extended {
TargetKind::ExtendedExample
} else {
TargetKind::Example
}
} else if extended {
TargetKind::ExtendedBinary
} else {
TargetKind::Binary
};
return (kind, new_manifest);
}
if file_contents.contains("#[test]") {
return (TargetKind::Test, new_manifest);
}
let kind = if example {
if extended {
TargetKind::UnknownExtendedExample
} else {
TargetKind::UnknownExample
}
} else if extended {
TargetKind::UnknownExtendedBinary
} else {
TargetKind::UnknownBinary
};
(kind, new_manifest)
}
pub fn is_extended_target(manifest_path: &Path, candidate: &Path) -> bool {
if let Some(project_root) = manifest_path.parent() {
candidate
.parent()
.map(|p| p != project_root)
.unwrap_or(false)
} else {
false
}
}
pub fn scan_directory_for_targets(scan_dir: &Path, be_silent: bool) -> Vec<CargoTarget> {
let mut targets = Vec::new();
let mut dirs_to_visit = vec![scan_dir.to_path_buf()];
let mut manifest_paths = Vec::new();
while let Some(current_dir) = dirs_to_visit.pop() {
if let Ok(entries) = fs::read_dir(¤t_dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
if path.file_name().map_or(false, |name| {
name == "node_modules" || name == "target" || name == "build"
}) {
continue;
}
dirs_to_visit.push(path); } else if path.file_name().map_or(false, |name| name == "Cargo.toml") {
manifest_paths.push(Some(path));
}
}
} else if !be_silent {
eprintln!("Failed to read directory: {}", current_dir.display());
}
}
if !be_silent {
for manifest in &manifest_paths {
if let Some(path) = manifest {
println!("Found Cargo.toml at: {}", path.display());
}
}
}
if !manifest_paths.is_empty() {
#[cfg(feature = "concurrent")]
{
let file_targets = crate::e_collect::collect_all_targets_parallel(
manifest_paths,
false, std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(4),
be_silent,
)
.unwrap_or_default();
if !be_silent {
println!(
"Found {} targets in scanned directories",
file_targets.len()
);
}
targets.extend(file_targets);
}
#[cfg(not(feature = "concurrent"))]
{
for manifest in manifest_paths {
if let Some(path) = manifest {
match crate::e_collect::collect_all_targets(
Some(path.clone()),
false, std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(4),
false, false, ) {
Ok(file_targets) => {
if !be_silent {
println!(
"Found {} targets in {}",
file_targets.len(),
path.display()
);
}
targets.extend(file_targets);
}
Err(e) => {
eprintln!("Error processing {}: {}", path.display(), e);
}
}
}
}
}
}
targets
}