#![deny(missing_docs)]
use cargo_metadata::{Metadata, MetadataCommand, Package};
use failure::format_err;
use log::debug;
use serde::de::DeserializeOwned;
use serde::Deserialize;
use serde_json::Value;
pub mod error {
pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
}
use crate::error::*;
#[derive(Deserialize, Debug)]
#[serde(rename_all = "kebab-case")]
pub struct PackConfig {
pub files: Option<Vec<String>>,
pub default_packers: Option<Vec<String>>,
}
pub struct CargoPack {
package_name: Option<String>,
pack_config: PackConfig,
metadata: Metadata,
}
fn lookup(mut value: Value, path: &[&str]) -> Option<Value> {
for key in path {
match value {
Value::Object(mut hm) => match hm.remove(*key) {
Some(v) => value = v,
None => return None,
},
Value::Array(mut v) => match key.parse::<usize>().ok() {
Some(idx) if idx < v.len() => value = v.remove(idx),
_ => return None,
},
_ => return None,
}
}
Some(value)
}
impl CargoPack {
pub fn new<P: Into<Option<String>>>(package_name: P) -> Result<Self> {
let package_name = package_name.into();
let metadata = MetadataCommand::new().no_deps().exec()?;
let pack_config: PackConfig = Self::decode_from_manifest_static(
&metadata,
package_name.as_ref().map(|s| s.as_ref()),
)?;
debug!("config: {:?}", pack_config);
Ok(CargoPack {
pack_config,
package_name,
metadata,
})
}
pub fn metadata(&self) -> &Metadata {
&self.metadata
}
pub fn config(&self) -> &PackConfig {
&self.pack_config
}
pub fn package(&self) -> Result<&Package> {
Self::find_package(
self.metadata(),
self.package_name.as_ref().map(AsRef::as_ref),
)
}
fn find_package<'a, 'b>(
metadata: &'a Metadata,
package_name: Option<&'b str>,
) -> Result<&'a Package> {
if let Some(ref name) = package_name {
let packages = metadata
.packages
.iter()
.filter(|p| &p.name == name)
.collect::<Vec<_>>();
match packages.len() {
0 => return Err(format_err!("unknown package {}", name)),
1 => Ok(packages[0]),
_ => return Err(format_err!("ambiguous name {}", name)),
}
} else {
match metadata.packages.len() {
1 => Ok(&metadata.packages[0]),
_ => return Err(format_err!("virtual hogehoge")),
}
}
}
fn decode_from_manifest_static<T: DeserializeOwned>(
metadata: &Metadata,
package_name: Option<&str>,
) -> Result<T> {
let package = Self::find_package(metadata, package_name)?;
debug!("package: {:?}", package);
let data = lookup(package.metadata.clone(), &["pack"])
.expect("no package.metadata.pack found in Cargo.toml");
serde_json::from_value(data).map_err(Into::into)
}
pub fn decode_from_manifest<'a, T: DeserializeOwned>(&self) -> Result<T> {
let package_name = self.package_name.as_ref().map(|s| s.as_ref());
Self::decode_from_manifest_static(self.metadata(), package_name)
}
pub fn files(&self) -> &[String] {
self.pack_config
.files
.as_ref()
.map(AsRef::as_ref)
.unwrap_or(&[])
}
}