embeddenator_workspace/
workspace.rs1use anyhow::{Context, Result};
4use std::path::{Path, PathBuf};
5use walkdir::WalkDir;
6
7use crate::cargo::CargoManifest;
8
9#[derive(Debug)]
11pub struct WorkspaceScanner {
12 root: PathBuf,
13}
14
15impl WorkspaceScanner {
16 pub fn new(root: impl AsRef<Path>) -> Self {
18 Self {
19 root: root.as_ref().to_path_buf(),
20 }
21 }
22
23 pub fn find_manifests(&self) -> Result<Vec<CargoManifest>> {
25 let mut manifests = Vec::new();
26
27 for entry in WalkDir::new(&self.root)
28 .follow_links(false)
29 .into_iter()
30 .filter_entry(|e| {
31 let name = e.file_name().to_string_lossy();
32 !matches!(name.as_ref(), "target" | ".git" | "node_modules" | ".cargo")
34 })
35 {
36 let entry = entry.context("Failed to read directory entry")?;
37
38 if entry.file_type().is_file() && entry.file_name() == "Cargo.toml" {
39 match CargoManifest::load(entry.path()) {
40 Ok(manifest) => manifests.push(manifest),
41 Err(e) => {
42 eprintln!("Warning: Failed to parse {}: {}", entry.path().display(), e);
43 }
44 }
45 }
46 }
47
48 Ok(manifests)
49 }
50
51 pub fn find_embeddenator_packages(&self) -> Result<Vec<CargoManifest>> {
53 let all_manifests = self.find_manifests()?;
54
55 let mut packages: Vec<CargoManifest> = all_manifests
58 .into_iter()
59 .filter(|m| {
60 let path_str = m.path.to_string_lossy();
61 m.package_name.starts_with("embeddenator")
62 && !path_str.contains("/crates/")
63 && !path_str.contains("/target/")
64 })
65 .collect();
66
67 packages.sort_by(|a, b| a.package_name.cmp(&b.package_name));
68 Ok(packages)
69 }
70}