crate_paths_cli_core/backend/local/
mod.rs1pub mod error;
2
3use super::BackendError;
4use super::LocalBackendError;
5use crate::error::CoreError;
6use crate::item::ItemEntry;
7use crate::parser;
8use inflector::Inflector as _;
9use std::path::PathBuf;
10use std::process::Command;
11
12fn fetch_crate_all_items_html(crate_name: &str) -> Result<String, LocalBackendError> {
13 let cargo_status = Command::new("cargo")
14 .args(["doc", "--workspace"])
15 .status()
16 .map_err(LocalBackendError::CargoDocExecution)?;
17
18 if !cargo_status.success() {
19 return Err(LocalBackendError::CargoDocStatus(
20 cargo_status.code().unwrap_or(1),
21 ));
22 }
23
24 let locate_project_output = Command::new("cargo")
25 .args(["locate-project", "--workspace", "--message-format=plain"])
26 .output()
27 .map_err(LocalBackendError::CargoLocateProjectExecution)?;
28
29 if !locate_project_output.status.success() {
30 return Err(LocalBackendError::CargoLocateProjectStatus(
31 locate_project_output.status.code().unwrap_or(1),
32 ));
33 }
34
35 let cargo_toml_path_str = String::from_utf8(locate_project_output.stdout)
36 .map_err(LocalBackendError::CargoLocateProjectOutput)?;
37
38 let cargo_toml_path = PathBuf::from(cargo_toml_path_str.trim());
39
40 let workspace_root = cargo_toml_path
41 .parent()
42 .ok_or_else(|| LocalBackendError::WorkspaceRootNotFound(cargo_toml_path.clone()))?;
43
44 let base_doc_path = workspace_root.join("target").join("doc");
45 let possible_names = [crate_name, &crate_name.to_snake_case()];
46
47 const ALL_HTML: &str = "all.html";
48
49 let doc_file_path = possible_names
50 .iter()
51 .map(|name| base_doc_path.join(name).join(ALL_HTML))
52 .find(|path| path.exists())
53 .ok_or_else(|| {
54 LocalBackendError::DocFileNotFound(base_doc_path.join(crate_name).join(ALL_HTML))
55 })?;
56
57 std::fs::read_to_string(&doc_file_path)
58 .map_err(|e| LocalBackendError::FileRead(doc_file_path.clone(), e))
59}
60
61pub fn process(crate_name: &str) -> Result<Vec<ItemEntry>, CoreError> {
62 let html_content = fetch_crate_all_items_html(crate_name)
63 .map_err(BackendError::from)
64 .map_err(CoreError::from)?;
65 parser::parse_html_to_items(crate_name, &html_content).map_err(CoreError::from)
66}
67
68pub fn get_crate_version(crate_name: &str) -> Option<String> {
69 let output = Command::new("cargo")
70 .args(["metadata", "--format-version", "1"])
71 .output()
72 .ok()?;
73
74 if !output.status.success() {
75 return None;
76 }
77
78 let metadata: serde_json::Value = serde_json::from_slice(&output.stdout).ok()?;
79 let packages = metadata.get("packages")?.as_array()?;
80
81 for package in packages {
82 #[allow(clippy::collapsible_if)]
84 if let Some(name) = package.get("name")?.as_str() {
85 if name == crate_name {
86 return package.get("version")?.as_str().map(|s| s.to_string());
87 }
88 }
89 }
90
91 None
92}