rust_docs_mcp/cache/
workspace.rs1use anyhow::{Context, Result, anyhow};
7use std::fs;
8use std::path::Path;
9use toml::Value;
10
11pub struct WorkspaceHandler;
13
14impl WorkspaceHandler {
15 pub fn is_workspace(cargo_toml_path: &Path) -> Result<bool> {
17 let content = fs::read_to_string(cargo_toml_path).with_context(|| {
18 format!("Failed to read Cargo.toml at {}", cargo_toml_path.display())
19 })?;
20
21 let parsed: Value = toml::from_str(&content).with_context(|| {
22 format!(
23 "Failed to parse Cargo.toml at {}",
24 cargo_toml_path.display()
25 )
26 })?;
27
28 if let Some(workspace) = parsed.get("workspace")
30 && let Some(members) = workspace.get("members")
31 && let Some(members_arr) = members.as_array()
32 {
33 return Ok(!members_arr.is_empty());
35 }
36
37 Ok(false)
38 }
39
40 pub fn get_workspace_members(cargo_toml_path: &Path) -> Result<Vec<String>> {
42 let content = fs::read_to_string(cargo_toml_path).with_context(|| {
43 format!("Failed to read Cargo.toml at {}", cargo_toml_path.display())
44 })?;
45
46 let parsed: Value = toml::from_str(&content).with_context(|| {
47 format!(
48 "Failed to parse Cargo.toml at {}",
49 cargo_toml_path.display()
50 )
51 })?;
52
53 let workspace = parsed
54 .get("workspace")
55 .ok_or_else(|| anyhow!("No [workspace] section found in Cargo.toml"))?;
56
57 let members = workspace
58 .get("members")
59 .and_then(|m| m.as_array())
60 .ok_or_else(|| anyhow!("No members array found in [workspace] section"))?;
61
62 let mut member_list = Vec::new();
63 for member in members {
64 if let Some(member_str) = member.as_str() {
65 if member_str.contains('*') {
67 if member_str == "examples/*" {
70 continue;
72 }
73 } else {
74 member_list.push(member_str.to_string());
75 }
76 }
77 }
78
79 Ok(member_list)
80 }
81
82 pub fn get_package_name(cargo_toml_path: &Path) -> Result<String> {
84 let content = fs::read_to_string(cargo_toml_path).with_context(|| {
85 format!("Failed to read Cargo.toml at {}", cargo_toml_path.display())
86 })?;
87
88 let parsed: Value = toml::from_str(&content).with_context(|| {
89 format!(
90 "Failed to parse Cargo.toml at {}",
91 cargo_toml_path.display()
92 )
93 })?;
94
95 let package = parsed
96 .get("package")
97 .ok_or_else(|| anyhow!("No [package] section found in Cargo.toml"))?;
98
99 let name = package
100 .get("name")
101 .and_then(|n| n.as_str())
102 .ok_or_else(|| anyhow!("No 'name' field found in [package] section"))?;
103
104 Ok(name.to_string())
105 }
106
107 pub fn get_package_version(cargo_toml_path: &Path) -> Result<String> {
109 let content = fs::read_to_string(cargo_toml_path).with_context(|| {
110 format!("Failed to read Cargo.toml at {}", cargo_toml_path.display())
111 })?;
112
113 let parsed: Value = toml::from_str(&content).with_context(|| {
114 format!(
115 "Failed to parse Cargo.toml at {}",
116 cargo_toml_path.display()
117 )
118 })?;
119
120 let package = parsed
121 .get("package")
122 .ok_or_else(|| anyhow!("No [package] section found in Cargo.toml"))?;
123
124 let version = package
125 .get("version")
126 .and_then(|v| v.as_str())
127 .ok_or_else(|| anyhow!("No 'version' field found in [package] section"))?;
128
129 Ok(version.to_string())
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136 use tempfile::TempDir;
137
138 #[test]
139 fn test_get_package_version() -> Result<()> {
140 let temp_dir = TempDir::new()?;
141
142 let cargo_toml = temp_dir.path().join("Cargo.toml");
144 fs::write(
145 &cargo_toml,
146 r#"
147[package]
148name = "test-crate"
149version = "1.2.3"
150"#,
151 )?;
152
153 let version = WorkspaceHandler::get_package_version(&cargo_toml)?;
154 assert_eq!(version, "1.2.3");
155
156 let no_version_toml = temp_dir.path().join("no_version.toml");
158 fs::write(
159 &no_version_toml,
160 r#"
161[package]
162name = "test-crate"
163"#,
164 )?;
165
166 let result = WorkspaceHandler::get_package_version(&no_version_toml);
167 assert!(result.is_err());
168 assert!(
169 result
170 .unwrap_err()
171 .to_string()
172 .contains("No 'version' field")
173 );
174
175 Ok(())
176 }
177
178 #[test]
179 fn test_workspace_detection() -> Result<()> {
180 let temp_dir = TempDir::new()?;
181
182 let workspace_toml = temp_dir.path().join("workspace.toml");
184 fs::write(
185 &workspace_toml,
186 r#"
187[workspace]
188members = ["crate-a", "crate-b"]
189"#,
190 )?;
191 assert!(WorkspaceHandler::is_workspace(&workspace_toml)?);
192
193 let crate_toml = temp_dir.path().join("crate.toml");
195 fs::write(
196 &crate_toml,
197 r#"
198[package]
199name = "my-crate"
200version = "0.1.0"
201"#,
202 )?;
203 assert!(!WorkspaceHandler::is_workspace(&crate_toml)?);
204
205 let mixed_toml = temp_dir.path().join("mixed.toml");
207 fs::write(
208 &mixed_toml,
209 r#"
210[package]
211name = "my-crate"
212version = "0.1.0"
213
214[workspace]
215members = ["sub-crate"]
216"#,
217 )?;
218 assert!(WorkspaceHandler::is_workspace(&mixed_toml)?); Ok(())
221 }
222}