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
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: name.to_string_lossy().to_string(),
60                            manifest_path: sub_manifest.to_string_lossy().to_string(),
61                            kind: TargetKind::Example,
62                            extended: true,
63                            origin: Some(TargetOrigin::SubProject(sub_manifest)),
64                        });
65                    }
66                }
67            }
68        }
69    }
70
71    // Additional discovery for binaries or tests can be added here.
72
73    Ok(targets)
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use std::fs;
80    use tempfile::tempdir;
81
82    #[test]
83    fn test_discover_targets_no_manifest() {
84        let temp = tempdir().unwrap();
85        // With no Cargo.toml, we expect an empty list.
86        let targets = discover_targets(temp.path()).unwrap();
87        assert!(targets.is_empty());
88    }
89
90    #[test]
91    fn test_discover_targets_with_manifest_and_example() {
92        let temp = tempdir().unwrap();
93        // Create a dummy Cargo.toml.
94        let manifest_path = temp.path().join("Cargo.toml");
95        fs::write(&manifest_path, "[package]\nname = \"dummy\"\n").unwrap();
96
97        // Create an examples directory with a dummy example file.
98        let examples_dir = temp.path().join("examples");
99        fs::create_dir(&examples_dir).unwrap();
100        let example_file = examples_dir.join("example1.rs");
101        fs::write(&example_file, "fn main() {}").unwrap();
102
103        let targets = discover_targets(temp.path()).unwrap();
104        // Expect at least two targets: one for the manifest and one for the example.
105        assert!(targets.len() >= 2);
106
107        let example_target = targets
108            .iter()
109            .find(|t| t.kind == TargetKind::Example && t.name == "example1");
110        assert!(example_target.is_some());
111    }
112}