1#![deny(missing_docs)]
14
15use cargo_metadata::{Metadata, MetadataCommand, Package};
16use failure::format_err;
17use log::debug;
18use serde::de::DeserializeOwned;
19use serde::Deserialize;
20use serde_json::Value;
21
22pub mod error {
24 pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
26}
27
28use crate::error::*;
29
30#[derive(Deserialize, Debug)]
40#[serde(rename_all = "kebab-case")]
41pub struct PackConfig {
42 pub files: Option<Vec<String>>,
44 pub default_packers: Option<Vec<String>>,
46}
47
48pub struct CargoPack {
50 package_name: Option<String>,
51 pack_config: PackConfig,
52 metadata: Metadata,
53}
54
55fn lookup(mut value: Value, path: &[&str]) -> Option<Value> {
56 for key in path {
57 match value {
58 Value::Object(mut hm) => match hm.remove(*key) {
59 Some(v) => value = v,
61 None => return None,
62 },
63 Value::Array(mut v) => match key.parse::<usize>().ok() {
64 Some(idx) if idx < v.len() => value = v.remove(idx),
66 _ => return None,
67 },
68 _ => return None,
69 }
70 }
71
72 Some(value)
73}
74
75impl CargoPack {
76 pub fn new<P: Into<Option<String>>>(package_name: P) -> Result<Self> {
84 let package_name = package_name.into();
85 let metadata = MetadataCommand::new().no_deps().exec()?;
86 let pack_config: PackConfig = Self::decode_from_manifest_static(
87 &metadata,
88 package_name.as_ref().map(|s| s.as_ref()),
89 )?;
90 debug!("config: {:?}", pack_config);
91 Ok(CargoPack {
92 pack_config,
93 package_name,
94 metadata,
95 })
96 }
97
98 pub fn metadata(&self) -> &Metadata {
100 &self.metadata
101 }
102
103 pub fn config(&self) -> &PackConfig {
105 &self.pack_config
106 }
107
108 pub fn package(&self) -> Result<&Package> {
110 Self::find_package(
111 self.metadata(),
112 self.package_name.as_ref().map(AsRef::as_ref),
113 )
114 }
115
116 fn find_package<'a, 'b>(
117 metadata: &'a Metadata,
118 package_name: Option<&'b str>,
119 ) -> Result<&'a Package> {
120 if let Some(ref name) = package_name {
121 let packages = metadata
122 .packages
123 .iter()
124 .filter(|p| &p.name == name)
125 .collect::<Vec<_>>();
126 match packages.len() {
127 0 => return Err(format_err!("unknown package {}", name)),
128 1 => Ok(packages[0]),
129 _ => return Err(format_err!("ambiguous name {}", name)),
130 }
131 } else {
132 match metadata.packages.len() {
133 1 => Ok(&metadata.packages[0]),
134 _ => return Err(format_err!("virtual hogehoge")),
135 }
136 }
137 }
138
139 fn decode_from_manifest_static<T: DeserializeOwned>(
140 metadata: &Metadata,
141 package_name: Option<&str>,
142 ) -> Result<T> {
143 let package = Self::find_package(metadata, package_name)?;
144 debug!("package: {:?}", package);
145 let data = lookup(package.metadata.clone(), &["pack"])
146 .expect("no package.metadata.pack found in Cargo.toml");
147 serde_json::from_value(data).map_err(Into::into)
148 }
149
150 pub fn decode_from_manifest<'a, T: DeserializeOwned>(&self) -> Result<T> {
152 let package_name = self.package_name.as_ref().map(|s| s.as_ref());
153 Self::decode_from_manifest_static(self.metadata(), package_name)
154 }
155
156 pub fn files(&self) -> &[String] {
158 self.pack_config
159 .files
160 .as_ref()
161 .map(AsRef::as_ref)
162 .unwrap_or(&[])
163 }
164}