1use serde::Deserialize;
2use std::path::Path;
3
4#[derive(Debug, Clone)]
6pub struct ProjectMeta {
7 pub name: String,
8 pub version: String,
9 pub binary_name: String,
10}
11
12#[derive(Deserialize)]
13struct CargoToml {
14 package: Option<PackageSection>,
15 bin: Option<Vec<BinSection>>,
16}
17
18#[derive(Deserialize)]
19struct PackageSection {
20 name: Option<String>,
21 version: Option<String>,
22}
23
24#[derive(Deserialize)]
25struct BinSection {
26 name: Option<String>,
27}
28
29impl ProjectMeta {
30 pub fn from_cargo_toml(project_dir: &Path) -> crate::Result<Self> {
32 let cargo_path = project_dir.join("Cargo.toml");
33 tracing::debug!(path = %cargo_path.display(), "reading Cargo.toml");
34
35 let content =
36 std::fs::read_to_string(&cargo_path).map_err(|e| crate::Error::CargoTomlRead {
37 path: cargo_path.clone(),
38 source: e,
39 })?;
40
41 let parsed: CargoToml =
42 toml::from_str(&content).map_err(|e| crate::Error::CargoTomlParse {
43 path: cargo_path.clone(),
44 source: e,
45 })?;
46
47 let package = parsed
48 .package
49 .ok_or_else(|| crate::Error::MissingPackageSection(cargo_path.clone()))?;
50
51 let name = package
52 .name
53 .ok_or_else(|| crate::Error::MissingPackageName(cargo_path.clone()))?;
54
55 let version = package.version.unwrap_or_else(|| "0.1.0".to_owned());
56
57 let binary_name = parsed
59 .bin
60 .and_then(|bins| bins.into_iter().next())
61 .and_then(|b| b.name)
62 .unwrap_or_else(|| name.clone());
63
64 tracing::debug!(
65 name = %name,
66 version = %version,
67 binary = %binary_name,
68 "project metadata resolved"
69 );
70
71 Ok(Self {
72 name,
73 version,
74 binary_name,
75 })
76 }
77}