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 let content =
34 std::fs::read_to_string(&cargo_path).map_err(|e| crate::Error::CargoTomlRead {
35 path: cargo_path.clone(),
36 source: e,
37 })?;
38
39 let parsed: CargoToml =
40 toml::from_str(&content).map_err(|e| crate::Error::CargoTomlParse {
41 path: cargo_path.clone(),
42 source: e,
43 })?;
44
45 let package = parsed
46 .package
47 .ok_or_else(|| crate::Error::MissingPackageSection(cargo_path.clone()))?;
48
49 let name = package
50 .name
51 .ok_or_else(|| crate::Error::MissingPackageName(cargo_path.clone()))?;
52
53 let version = package.version.unwrap_or_else(|| "0.1.0".to_owned());
54
55 let binary_name = parsed
57 .bin
58 .and_then(|bins| bins.into_iter().next())
59 .and_then(|b| b.name)
60 .unwrap_or_else(|| name.clone());
61
62 Ok(Self {
63 name,
64 version,
65 binary_name,
66 })
67 }
68}