cargo_e/
e_discovery.rs

1// src/e_discovery.rs
2use std::{fs, path::Path};
3
4use crate::e_target::{CargoTarget, TargetKind, TargetOrigin};
5use anyhow::{Context, Result};
6
7/// Discover targets in the given directory.
8/// This function scans for a Cargo.toml and then looks for example files or subproject directories.
9pub fn discover_targets(current_dir: &Path) -> Result<Vec<CargoTarget>> {
10    let mut targets = Vec::new();
11    let parent = current_dir.parent().expect("expected cwd to have a parent");
12    // Check if a Cargo.toml exists in the current directory.
13    let manifest_path = current_dir.join("Cargo.toml");
14    if manifest_path.exists() {
15        targets.push(CargoTarget {
16            name: "default".to_string(),
17            display_name: "Default Manifest".to_string(),
18            manifest_path: manifest_path.to_string_lossy().to_string(),
19            kind: TargetKind::Manifest,
20            extended: false,
21            origin: None,
22        });
23    }
24
25    // Scan the "examples" directory for example targets.
26    let examples_dir = current_dir.join("examples");
27    if examples_dir.exists() && examples_dir.is_dir() {
28        for entry in fs::read_dir(&examples_dir)
29            .with_context(|| format!("Reading directory {:?}", examples_dir))?
30        {
31            let entry = entry?;
32            let path = entry.path();
33            if path.is_file() {
34                // Assume that any .rs file in examples/ is an example.
35                if let Some(ext) = path.extension() {
36                    if ext == "rs" {
37                        if let Some(stem) = path.file_stem() {
38                            targets.push(CargoTarget {
39                                name: stem.to_string_lossy().to_string(),
40                                display_name: stem.to_string_lossy().to_string(),
41                                manifest_path: current_dir
42                                    .join("Cargo.toml")
43                                    .to_string_lossy()
44                                    .to_string(),
45                                kind: TargetKind::Example,
46                                extended: false,
47                                origin: Some(TargetOrigin::SingleFile(path)),
48                            });
49                        }
50                    }
51                }
52            } else if path.is_dir() {
53                // If the directory contains a Cargo.toml, treat it as an extended subproject.
54                let sub_manifest = path.join("Cargo.toml");
55                if sub_manifest.exists() {
56                    if let Some(name) = path.file_name() {
57                        targets.push(CargoTarget {
58                            name: name.to_string_lossy().to_string(),
59                            display_name: format!(
60                                "parent{} {}",
61                                parent.display(),
62                                name.to_string_lossy()
63                            ),
64                            manifest_path: sub_manifest.to_string_lossy().to_string(),
65                            kind: TargetKind::Example,
66                            extended: true,
67                            origin: Some(TargetOrigin::SubProject(sub_manifest)),
68                        });
69                    }
70                }
71            }
72        }
73    }
74
75    // Additional discovery for binaries or tests can be added here.
76
77    Ok(targets)
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83    use std::fs;
84    use tempfile::tempdir;
85
86    #[test]
87    fn test_discover_targets_no_manifest() {
88        let temp = tempdir().unwrap();
89        // With no Cargo.toml, we expect an empty list.
90        let targets = discover_targets(temp.path()).unwrap();
91        assert!(targets.is_empty());
92    }
93
94    #[test]
95    fn test_discover_targets_with_manifest_and_example() {
96        let temp = tempdir().unwrap();
97        // Create a dummy Cargo.toml.
98        let manifest_path = temp.path().join("Cargo.toml");
99        fs::write(&manifest_path, "[package]\nname = \"dummy\"\n").unwrap();
100
101        // Create an examples directory with a dummy example file.
102        let examples_dir = temp.path().join("examples");
103        fs::create_dir(&examples_dir).unwrap();
104        let example_file = examples_dir.join("example1.rs");
105        fs::write(&example_file, "fn main() {}").unwrap();
106
107        let targets = discover_targets(temp.path()).unwrap();
108        // Expect at least two targets: one for the manifest and one for the example.
109        assert!(targets.len() >= 2);
110
111        let example_target = targets
112            .iter()
113            .find(|t| t.kind == TargetKind::Example && t.name == "example1");
114        assert!(example_target.is_some());
115    }
116}