crate_seq_core/
workspace.rs1#[cfg(test)]
4#[path = "workspace_tests.rs"]
5mod tests;
6
7use std::path::{Path, PathBuf};
8
9use crate::Error;
10
11#[derive(Debug, Clone)]
13pub struct WorkspaceMember {
14 pub name: String,
16 pub manifest_path: PathBuf,
18 pub crate_dir: PathBuf,
20}
21
22fn read_file(path: &Path) -> Result<String, Error> {
24 std::fs::read_to_string(path).map_err(|source| Error::Io {
25 path: path.to_owned(),
26 source,
27 })
28}
29
30fn parse_toml(content: &str, path: &Path) -> Result<toml_edit::DocumentMut, Error> {
32 content
33 .parse::<toml_edit::DocumentMut>()
34 .map_err(|source| Error::Toml {
35 path: path.to_owned(),
36 source,
37 })
38}
39
40fn package_name(doc: &toml_edit::DocumentMut, manifest_path: &Path) -> Result<String, Error> {
42 doc.get("package")
43 .and_then(|p| p.get("name"))
44 .and_then(|n| n.as_str())
45 .map(str::to_owned)
46 .ok_or_else(|| Error::MissingPackageName(manifest_path.to_owned()))
47}
48
49fn member_from_dir(member_dir: PathBuf) -> Result<WorkspaceMember, Error> {
51 let manifest_path = member_dir.join("Cargo.toml");
52 if !manifest_path.exists() {
53 return Err(Error::Io {
54 source: std::io::Error::new(std::io::ErrorKind::NotFound, "Cargo.toml not found"),
55 path: manifest_path,
56 });
57 }
58 let content = read_file(&manifest_path)?;
59 let doc = parse_toml(&content, &manifest_path)?;
60 let name = package_name(&doc, &manifest_path)?;
61 Ok(WorkspaceMember {
62 name,
63 manifest_path,
64 crate_dir: member_dir,
65 })
66}
67
68fn expand_member_path(workspace_root: &Path, raw: &str) -> Vec<PathBuf> {
74 if let Some(prefix) = raw.strip_suffix("/*") {
75 let parent = workspace_root.join(prefix);
76 match std::fs::read_dir(&parent) {
77 Ok(entries) => entries
78 .filter_map(std::result::Result::ok)
79 .map(|e| e.path())
80 .filter(|p| p.join("Cargo.toml").exists())
81 .collect(),
82 Err(_) => vec![],
83 }
84 } else {
85 let candidate = workspace_root.join(raw);
86 if candidate.join("Cargo.toml").exists() {
87 vec![candidate]
88 } else {
89 vec![]
90 }
91 }
92}
93
94pub fn discover_members(workspace_root: &Path) -> Result<Vec<WorkspaceMember>, Error> {
106 let root_manifest = workspace_root.join("Cargo.toml");
107 let content = read_file(&root_manifest)?;
108 let doc = parse_toml(&content, &root_manifest)?;
109
110 if let Some(members_array) = doc
111 .get("workspace")
112 .and_then(|w| w.get("members"))
113 .and_then(|m| m.as_array())
114 {
115 let raw_paths: Vec<String> = members_array
116 .iter()
117 .filter_map(|v| v.as_str().map(str::to_owned))
118 .collect();
119
120 let mut members = Vec::new();
121 for raw in &raw_paths {
122 for dir in expand_member_path(workspace_root, raw) {
123 members.push(member_from_dir(dir)?);
124 }
125 }
126 Ok(members)
127 } else {
128 let name = package_name(&doc, &root_manifest)?;
129 Ok(vec![WorkspaceMember {
130 name,
131 manifest_path: root_manifest,
132 crate_dir: workspace_root.to_owned(),
133 }])
134 }
135}