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