1use anyhow::anyhow;
2
3fn cargo_toml_error(message: &str) -> anyhow::Error {
4 anyhow!("could not read Cargo.toml: {}", message)
5}
6
7#[derive(Debug)]
9pub struct Config {
10 package_name: String,
11 package_version: String,
12 metadata: Metadata,
13}
14
15impl Config {
16 pub fn from_manifest(manifest: &toml::value::Table) -> anyhow::Result<Self> {
18 let malformed_package = |e| anyhow!("malformed package table: {}", e);
19 let package = get_key(manifest, "package", toml_table)?;
20 let package_name = get_key(package, "name", toml_str).map_err(malformed_package)?;
21 let package_version = get_key(package, "version", toml_str).map_err(malformed_package)?;
22 let parcel = match get_key_opt(package, "metadata", toml_table)? {
23 Some(m) => get_key_opt(m, "parcel", toml_table)?,
24 None => None,
25 };
26 let metadata = if let Some(metadata) = parcel {
27 Metadata::from_toml(metadata)
28 .map_err(|e| anyhow!("malformed parcel metadata: {}", e))?
29 } else {
30 Default::default()
31 };
32 Ok(Config {
33 package_name: package_name.into(),
34 package_version: package_version.into(),
35 metadata,
36 })
37 }
38
39 pub fn package_name(&self) -> &str {
44 self.metadata
45 .pkg_name
46 .as_ref()
47 .map(|s| s.as_str())
48 .unwrap_or(&self.package_name)
49 }
50
51 pub fn package_version(&self) -> &str {
53 &self.package_version
54 }
55
56 pub fn cargo_binaries(&self) -> impl Iterator<Item = &str> {
58 self.metadata.cargo_binaries.iter().map(|s| s.as_ref())
59 }
60
61 pub fn man_pages(&self) -> impl Iterator<Item = &str> {
63 self.metadata.man_pages.iter().map(|s| s.as_ref())
64 }
65
66 pub fn pkg_data(&self) -> impl Iterator<Item = &str> {
68 self.metadata.pkg_data.iter().map(|s| s.as_ref())
69 }
70}
71
72fn toml_str_array(value: &toml::Value) -> anyhow::Result<Vec<String>> {
73 let array = value
74 .as_array()
75 .ok_or_else(|| anyhow!("must be an array"))?;
76 array
77 .iter()
78 .map(|item| {
79 item.as_str()
80 .map(ToOwned::to_owned)
81 .ok_or_else(|| anyhow!("contains non-string item `{}`", item))
82 })
83 .collect()
84}
85
86#[derive(Debug, Default)]
87struct Metadata {
88 pkg_name: Option<String>,
89 cargo_binaries: Vec<String>,
90 man_pages: Vec<String>,
91 pkg_data: Vec<String>,
92}
93
94impl Metadata {
95 fn from_toml(metadata: &toml::value::Table) -> anyhow::Result<Self> {
96 Ok(Metadata {
97 pkg_name: get_key_opt(metadata, "pkg-name", toml_str)?.map(|s| s.to_owned()),
98 cargo_binaries: get_key_opt(metadata, "cargo-binaries", toml_str_array)?
99 .unwrap_or_default(),
100 man_pages: get_key_opt(metadata, "man-pages", toml_str_array)?.unwrap_or_default(),
101 pkg_data: get_key_opt(metadata, "pkg-data", toml_str_array)?.unwrap_or_default(),
102 })
103 }
104}
105
106fn toml_table(value: &toml::Value) -> anyhow::Result<&toml::value::Table> {
107 value.as_table().ok_or_else(|| anyhow!("must be a table"))
108}
109
110fn toml_str(value: &toml::Value) -> anyhow::Result<&str> {
111 value.as_str().ok_or_else(|| anyhow!("must be a string"))
112}
113
114fn get_key<'a, F, T>(table: &'a toml::value::Table, name: &str, f: F) -> anyhow::Result<T>
115where
116 F: FnOnce(&'a toml::Value) -> anyhow::Result<T>,
117{
118 get_key_opt(table, name, f)?.ok_or_else(|| cargo_toml_error("required key `{}` missing"))
119}
120
121fn get_key_opt<'a, F, T>(
122 table: &'a toml::value::Table,
123 name: &str,
124 f: F,
125) -> anyhow::Result<Option<T>>
126where
127 F: FnOnce(&'a toml::Value) -> anyhow::Result<T>,
128{
129 table
130 .get(name)
131 .map(|v| f(v).map_err(|e| anyhow!("invalid value for key `{}`: {}", name, e)))
132 .transpose()
133}