use anyhow::anyhow;
fn cargo_toml_error(message: &str) -> anyhow::Error {
anyhow!("could not read Cargo.toml: {}", message)
}
#[derive(Debug)]
pub struct Config {
package_name: String,
package_version: String,
metadata: Metadata,
}
impl Config {
pub fn from_manifest(manifest: &toml::value::Table) -> anyhow::Result<Self> {
let malformed_package = |e| anyhow!("malformed package table: {}", e);
let package = get_key(manifest, "package", toml_table)?;
let package_name = get_key(package, "name", toml_str).map_err(malformed_package)?;
let package_version = get_key(package, "version", toml_str).map_err(malformed_package)?;
let parcel = match get_key_opt(package, "metadata", toml_table)? {
Some(m) => get_key_opt(m, "parcel", toml_table)?,
None => None,
};
let metadata = if let Some(metadata) = parcel {
Metadata::from_toml(metadata)
.map_err(|e| anyhow!("malformed parcel metadata: {}", e))?
} else {
Default::default()
};
Ok(Config {
package_name: package_name.into(),
package_version: package_version.into(),
metadata,
})
}
pub fn package_name(&self) -> &str {
self.metadata
.pkg_name
.as_ref()
.map(|s| s.as_str())
.unwrap_or(&self.package_name)
}
pub fn package_version(&self) -> &str {
&self.package_version
}
pub fn cargo_binaries(&self) -> impl Iterator<Item = &str> {
self.metadata.cargo_binaries.iter().map(|s| s.as_ref())
}
pub fn man_pages(&self) -> impl Iterator<Item = &str> {
self.metadata.man_pages.iter().map(|s| s.as_ref())
}
pub fn pkg_data(&self) -> impl Iterator<Item = &str> {
self.metadata.pkg_data.iter().map(|s| s.as_ref())
}
}
fn toml_str_array(value: &toml::Value) -> anyhow::Result<Vec<String>> {
let array = value
.as_array()
.ok_or_else(|| anyhow!("must be an array"))?;
array
.iter()
.map(|item| {
item.as_str()
.map(ToOwned::to_owned)
.ok_or_else(|| anyhow!("contains non-string item `{}`", item))
})
.collect()
}
#[derive(Debug, Default)]
struct Metadata {
pkg_name: Option<String>,
cargo_binaries: Vec<String>,
man_pages: Vec<String>,
pkg_data: Vec<String>,
}
impl Metadata {
fn from_toml(metadata: &toml::value::Table) -> anyhow::Result<Self> {
Ok(Metadata {
pkg_name: get_key_opt(metadata, "pkg-name", toml_str)?.map(|s| s.to_owned()),
cargo_binaries: get_key_opt(metadata, "cargo-binaries", toml_str_array)?
.unwrap_or_default(),
man_pages: get_key_opt(metadata, "man-pages", toml_str_array)?.unwrap_or_default(),
pkg_data: get_key_opt(metadata, "pkg-data", toml_str_array)?.unwrap_or_default(),
})
}
}
fn toml_table(value: &toml::Value) -> anyhow::Result<&toml::value::Table> {
value.as_table().ok_or_else(|| anyhow!("must be a table"))
}
fn toml_str(value: &toml::Value) -> anyhow::Result<&str> {
value.as_str().ok_or_else(|| anyhow!("must be a string"))
}
fn get_key<'a, F, T>(table: &'a toml::value::Table, name: &str, f: F) -> anyhow::Result<T>
where
F: FnOnce(&'a toml::Value) -> anyhow::Result<T>,
{
get_key_opt(table, name, f)?.ok_or_else(|| cargo_toml_error("required key `{}` missing"))
}
fn get_key_opt<'a, F, T>(
table: &'a toml::value::Table,
name: &str,
f: F,
) -> anyhow::Result<Option<T>>
where
F: FnOnce(&'a toml::Value) -> anyhow::Result<T>,
{
table
.get(name)
.map(|v| f(v).map_err(|e| anyhow!("invalid value for key `{}`: {}", name, e)))
.transpose()
}