playdate_build/metadata/
source.rs

1use std::hash::Hash;
2use std::borrow::Cow;
3use std::path::Path;
4
5use super::format::{AssetsOptions, AssetsRules, Ext, ExtraFields, ExtraValue, Manifest, Options, Support};
6use super::format::ws::OptionsDefault;
7
8
9pub trait PackageSource {
10	type Authors: ?Sized + std::slice::Join<&'static str, Output = String>;
11	type Metadata: MetadataSource;
12
13
14	/// Crate name.
15	fn name(&self) -> Cow<str>;
16	/// Crate authors.
17	fn authors(&self) -> &Self::Authors;
18	// fn authors(&self) -> &[&str];
19	/// Crate version (semver).
20	fn version(&self) -> Cow<str>;
21	/// Crate description.
22	fn description(&self) -> Option<Cow<str>>;
23	/// Crate metadata - `playdate` table.
24	fn metadata(&self) -> Option<&Self::Metadata>;
25
26	/// [`Options`] used as default.
27	///
28	/// Usually it may be from `workspace.metadata.playdate.options` or some external config,
29	/// depends on implementation.
30	///
31	/// If this __and__ `metadata.options` is `None` - [`Options::default()`] is used.
32	fn default_options(&self) -> Option<&OptionsDefault> { None }
33
34	/// Cloned or default [`AssetsOptions`] from `metadata.options`,
35	/// merged with `default_options`,
36	/// if `metadata.options.workspace` is `true`.
37	fn assets_options(&self) -> AssetsOptions {
38		self.metadata()
39		    .map(|m| m.options().with_workspace(self.default_options()))
40		    .unwrap_or_default()
41		    .assets
42		    .to_owned()
43	}
44
45	/// Names of `bin` cargo-targets.
46	fn bins(&self) -> &[&str];
47	/// Names of `example` cargo-targets.
48	fn examples(&self) -> &[&str];
49
50	/// Crate manifest path (Cargo.toml).
51	fn manifest_path(&self) -> Cow<Path>;
52
53
54	fn manifest_for_crate(&self) -> impl ManifestSourceOptExt {
55		use super::format::Manifest;
56		use std::slice::Join;
57
58		let author = {
59			let author = Join::join(self.authors(), ", ");
60			if author.trim().is_empty() {
61				None
62			} else {
63				Some(author.into())
64			}
65		};
66		let version = Some(self.version());
67		let package = Manifest { name: Some(self.name()),
68		                         description: self.description(),
69		                         author,
70		                         version,
71		                         bundle_id: None,
72		                         image_path: None,
73		                         launch_sound_path: None,
74		                         content_warning: None,
75		                         content_warning2: None,
76		                         build_number: None };
77
78		if let Some(meta) = self.metadata() {
79			let manifest = meta.manifest();
80			let base = Ext { main: package,
81			                 extra: Default::default() };
82			// TODO: Reduce coping, return associated type instead with all strings in the Cow<'self>.
83			// Also get merged manifest with refs, using `override_with_extra_ref`
84			let result = base.override_with_extra(manifest);
85			Ext { main: Manifest::from(&result),
86			      extra: result.iter_extra()
87			                   .map(|m| {
88				                   m.into_iter()
89				                    .map(|(k, v)| (k.as_ref().to_owned(), v.as_ref().clone()))
90				                    .collect()
91			                   })
92			                   .unwrap_or_default() }
93		} else {
94			Ext { main: package.into_owned(),
95			      extra: Default::default() }
96		}
97	}
98
99
100	/// Returns `None` if manifest for `target` not found, no fallback.
101	fn manifest_override_for(&self, target: &str, dev: bool) -> Option<Ext<Manifest<String>>> {
102		let base = self.manifest_for_crate();
103
104		if let Some(root) = self.metadata() {
105			if dev {
106				if let Some(man) = root.example(target) {
107					Some(base.override_with_extra(man).into_owned())
108				} else {
109					log::debug!("dev-target override not found for {target:?}");
110					None
111				}
112			} else if let Some(man) = root.bin(target) {
113				Some(base.override_with_extra(man).into_owned())
114			} else {
115				log::debug!("target override not found for {target:?}");
116				None
117			}
118		} else {
119			Some(base.into_owned())
120		}
121	}
122
123	/// Returns manifest for specified `target`. If not found, returns manifest for crate.
124	fn manifest_override_or_crate(&self, target: Option<&str>, dev: bool) -> Ext<Manifest<String>> {
125		target.and_then(|target| self.manifest_override_for(target, dev))
126		      .unwrap_or_else(|| self.manifest_for_crate().into_owned())
127	}
128}
129
130
131pub trait MetadataSource {
132	type S: Eq + Hash;
133	type Manifest: ManifestSourceOptExt;
134	type TargetManifest: ManifestSourceOptExt + TargetId;
135
136	/// Main manifest, default and base for all cargo-targets.
137	fn manifest(&self) -> &Self::Manifest;
138
139	/// All manifests for "bin" cargo-targets.
140	/// Overrides main manifest field-by-field.
141	fn bins(&self) -> &[Self::TargetManifest];
142	/// All manifests for "example" cargo-targets.
143	/// Overrides main manifest field-by-field.
144	fn examples(&self) -> &[Self::TargetManifest];
145
146	/// Manifest for specified "bin" cargo-target.
147	/// Overrides main manifest field-by-field.
148	fn bin<'t>(&'t self, target: &'_ str) -> Option<&'t Self::TargetManifest> {
149		self.bins().iter().find(|b| b.target() == target)
150	}
151	/// Manifest for specified "example" cargo-target.
152	/// Overrides main manifest field-by-field.
153	fn example<'t>(&'t self, target: &'_ str) -> Option<&'t Self::TargetManifest> {
154		self.examples().iter().find(|b| b.target() == target)
155	}
156
157	fn bin_targets(&self) -> impl IntoIterator<Item = &str>;
158	fn example_targets(&self) -> impl IntoIterator<Item = &str>;
159	fn all_targets(&self) -> impl IntoIterator<Item = &str> {
160		self.bin_targets().into_iter().chain(self.example_targets())
161	}
162
163	fn bins_iter(&self) -> Option<impl Iterator<Item = &Self::TargetManifest>> {
164		(!self.bins().is_empty()).then_some(self.bins().iter())
165	}
166	fn examples_iter(&self) -> Option<impl Iterator<Item = &Self::TargetManifest>> {
167		(!self.examples().is_empty()).then_some(self.examples().iter())
168	}
169
170	fn all_targets_iter(&self) -> impl Iterator<Item = &Self::TargetManifest> {
171		self.bins_iter()
172		    .into_iter()
173		    .flatten()
174		    .chain(self.examples_iter().into_iter().flatten())
175	}
176
177	fn assets(&self) -> &AssetsRules<Self::S>;
178	fn dev_assets(&self) -> &AssetsRules<Self::S>;
179
180	fn options(&self) -> &Options;
181	fn assets_options(&self) -> Cow<'_, AssetsOptions>;
182
183	fn support(&self) -> &Support;
184
185	/// Make a manifest for a specific target, merged with base manifest for package.
186	/// Returns `None` if the target is not found.
187	fn manifest_for_target(&self, target: &str, dev: bool) -> Option<impl ManifestSourceOptExt> {
188		// manifest() returns T without lifetime, so can't be associated with `&self`,
189		// that should be fixed
190		let base = self.manifest();
191
192		if dev {
193			if let Some(target) = self.example(target) {
194				let trg = base.override_with_extra_ref(target);
195				Some(trg.into_owned())
196			} else {
197				None
198			}
199		} else if let Some(target) = self.bin(target) {
200			let trg = base.override_with_extra_ref(target);
201			Some(trg.into_owned())
202		} else {
203			None
204		}
205	}
206
207	fn manifest_for_target_any(&self, target: &str) -> Option<impl ManifestSourceOptExt> {
208		self.manifest_for_target(target, false)
209		    .or_else(|| self.manifest_for_target(target, true))
210		    .map(|m| m.into_manifest())
211		    .or_else(|| Some(Ext::<Manifest<String>>::from(self.manifest())))
212	}
213}
214
215
216impl<T: MetadataSource> MetadataSource for &T {
217	type S = <T as MetadataSource>::S;
218	type Manifest = <T as MetadataSource>::Manifest;
219	type TargetManifest = <T as MetadataSource>::TargetManifest;
220
221
222	fn manifest(&self) -> &Self::Manifest { (*self).manifest() }
223
224	fn bins(&self) -> &[Self::TargetManifest] { <T as MetadataSource>::bins(*self) }
225	fn examples(&self) -> &[Self::TargetManifest] { <T as MetadataSource>::examples(*self) }
226
227	fn bin_targets(&self) -> impl IntoIterator<Item = &str> { (*self).bin_targets() }
228	fn example_targets(&self) -> impl IntoIterator<Item = &str> { (*self).example_targets() }
229
230	fn assets(&self) -> &AssetsRules<Self::S> { (*self).assets() }
231	fn dev_assets(&self) -> &AssetsRules<Self::S> { (*self).dev_assets() }
232	fn options(&self) -> &Options { (*self).options() }
233	fn assets_options(&self) -> Cow<'_, AssetsOptions> { (*self).assets_options() }
234	fn support(&self) -> &Support { (*self).support() }
235}
236
237
238pub trait ManifestSource {
239	fn name(&self) -> &str;
240	fn version(&self) -> &str;
241	fn author(&self) -> &str;
242	fn bundle_id(&self) -> &str;
243	fn description(&self) -> &str;
244	fn image_path(&self) -> &str;
245	fn launch_sound_path(&self) -> &str;
246	fn content_warning(&self) -> &str;
247	fn content_warning2(&self) -> &str;
248	fn build_number(&self) -> Option<usize>;
249}
250
251
252pub trait ManifestSourceOpt {
253	/// Possibly incomplete, that means that some of values could be `None`.
254	const MAY_BE_INCOMPLETE: bool;
255
256	fn name(&self) -> Option<&str>;
257	fn version(&self) -> Option<&str>;
258	fn author(&self) -> Option<&str>;
259	fn bundle_id(&self) -> Option<&str>;
260	fn description(&self) -> Option<&str>;
261	fn image_path(&self) -> Option<&str>;
262	fn launch_sound_path(&self) -> Option<&str>;
263	fn content_warning(&self) -> Option<&str>;
264	fn content_warning2(&self) -> Option<&str>;
265	fn build_number(&self) -> Option<usize>;
266
267	fn override_with<'a, Over>(&'a self, over: &'a Over) -> impl ManifestSourceOpt + 'a
268		where Over: ManifestSourceOpt {
269		use super::format::Manifest;
270
271		Manifest::<Cow<str>> { name: over.name().or(self.name()).map(Into::into),
272		                       version: over.version().or(self.version()).map(Into::into),
273		                       author: over.author().or(self.author()).map(Into::into),
274		                       bundle_id: over.bundle_id().or(self.bundle_id()).map(Into::into),
275		                       description: over.description().or(self.description()).map(Into::into),
276		                       image_path: over.image_path().or(self.image_path()).map(Into::into),
277		                       launch_sound_path: over.launch_sound_path()
278		                                              .or(self.launch_sound_path())
279		                                              .map(Into::into),
280		                       content_warning: over.content_warning().or(self.content_warning()).map(Into::into),
281		                       content_warning2: over.content_warning2()
282		                                             .or(self.content_warning2())
283		                                             .map(Into::into),
284		                       build_number: over.build_number().or(self.build_number()) }
285	}
286}
287
288
289pub trait ManifestSourceOptExt: ManifestSourceOpt {
290	const MAY_HAVE_EXTRA: bool;
291
292	fn has_extra(&self) -> bool { Self::MAY_HAVE_EXTRA && self.iter_extra().is_some() }
293	fn iter_extra(&self) -> Option<impl IntoIterator<Item = (impl AsRef<str>, impl AsRef<ExtraValue>)>>;
294
295	fn override_with_extra_ref<'t, Over>(&'t self, over: &'t Over) -> impl ManifestSourceOptExt + 't
296		where Over: ManifestSourceOptExt {
297		let manifest = self.override_with(over);
298		let extra = if over.has_extra() || self.has_extra() {
299			match (self.iter_extra(), over.iter_extra()) {
300				(None, None) => None,
301				(None, Some(extra)) => {
302					let result = extra.into_iter()
303					                  .map(|(k, v)| (k.as_ref().to_owned(), v.as_ref().clone()));
304					Some(result.collect())
305				},
306				(Some(extra), None) => {
307					let result = extra.into_iter()
308					                  .map(|(k, v)| (k.as_ref().to_owned(), v.as_ref().clone()));
309					Some(result.collect())
310				},
311				(Some(base), Some(extra)) => {
312					let mut result: ExtraFields<ExtraValue> = base.into_iter()
313					                                              .map(|(k, v)| (k.as_ref().to_owned(), v.as_ref().clone()))
314					                                              .collect();
315					result.extend(
316					              extra.into_iter()
317					                   .map(|(k, v)| (k.as_ref().to_owned(), v.as_ref().clone())),
318					);
319					Some(result)
320				},
321			}.unwrap_or_else(|| ExtraFields::with_capacity(0))
322		} else {
323			ExtraFields::with_capacity(0)
324		};
325
326		Ext { main: manifest,
327		      extra }
328	}
329
330
331	fn override_with_extra<Over: ManifestSourceOptExt>(&self,
332	                                                   overrider: &Over)
333	                                                   -> impl ManifestSourceOptExt + Cob<'static> {
334		self.override_with_extra_ref(overrider).into_manifest()
335	}
336}
337
338
339impl<T: ManifestSource> ManifestSourceOpt for T {
340	const MAY_BE_INCOMPLETE: bool = false;
341	fn name(&self) -> Option<&str> { Some(ManifestSource::name(self)) }
342	fn version(&self) -> Option<&str> { Some(ManifestSource::version(self)) }
343	fn author(&self) -> Option<&str> { Some(ManifestSource::author(self)) }
344	fn bundle_id(&self) -> Option<&str> { Some(ManifestSource::bundle_id(self)) }
345	fn description(&self) -> Option<&str> { Some(ManifestSource::description(self)) }
346	fn image_path(&self) -> Option<&str> { Some(ManifestSource::image_path(self)) }
347	fn launch_sound_path(&self) -> Option<&str> { Some(ManifestSource::launch_sound_path(self)) }
348	fn content_warning(&self) -> Option<&str> { Some(ManifestSource::content_warning(self)) }
349	fn content_warning2(&self) -> Option<&str> { Some(ManifestSource::content_warning2(self)) }
350	fn build_number(&self) -> Option<usize> { ManifestSource::build_number(self) }
351}
352
353impl<T: Clone + ManifestSourceOpt> ManifestSourceOpt for Cow<'_, T> {
354	const MAY_BE_INCOMPLETE: bool = T::MAY_BE_INCOMPLETE;
355
356	fn name(&self) -> Option<&str> { self.as_ref().name() }
357	fn version(&self) -> Option<&str> { self.as_ref().version() }
358	fn author(&self) -> Option<&str> { self.as_ref().author() }
359	fn bundle_id(&self) -> Option<&str> { self.as_ref().bundle_id() }
360	fn description(&self) -> Option<&str> { self.as_ref().description() }
361	fn image_path(&self) -> Option<&str> { self.as_ref().image_path() }
362	fn launch_sound_path(&self) -> Option<&str> { self.as_ref().launch_sound_path() }
363	fn content_warning(&self) -> Option<&str> { self.as_ref().content_warning() }
364	fn content_warning2(&self) -> Option<&str> { self.as_ref().content_warning2() }
365	fn build_number(&self) -> Option<usize> { self.as_ref().build_number() }
366}
367
368impl<'t, T: ManifestSourceOptExt> ManifestSourceOptExt for &'t T where &'t T: ManifestSourceOpt {
369	const MAY_HAVE_EXTRA: bool = true;
370
371	fn iter_extra(&self) -> Option<impl IntoIterator<Item = (impl AsRef<str>, impl AsRef<ExtraValue>)>> {
372		(*self).iter_extra()
373	}
374}
375
376
377pub trait TargetId {
378	fn target(&self) -> &str;
379}
380
381
382pub trait IntoManifest: Sized + ManifestSourceOptExt {
383	fn into_manifest(self) -> Ext<Manifest<String>> { self.into_owned() }
384}
385impl<T: ManifestSourceOptExt> IntoManifest for T {}
386
387
388pub(super) trait IntoOwned<T> {
389	fn into_owned(self) -> T;
390}
391
392
393/// Cob as CopyBorrow - partially copy, partially borrow.
394/// Used to produce instance of type with internally borrowed things from `self`.
395pub trait Cob<'t>
396	where Self::Output: 't {
397	type Output;
398	fn as_borrow(&'t self) -> Self::Output;
399}
400
401impl<'t> Cob<'t> for str where Self: 't {
402	type Output = Cow<'t, str>;
403	fn as_borrow(&'t self) -> Self::Output { self.into() }
404}
405
406impl<'t, S: AsRef<str>> Cob<'t> for S {
407	type Output = Cow<'t, str>;
408	fn as_borrow(&'t self) -> Self::Output { self.as_ref().into() }
409}
410
411
412#[cfg(test)]
413mod tests {
414	use std::collections::HashMap;
415
416	use crate::metadata::format::Manifest;
417	use crate::metadata::format::Override;
418	use crate::metadata::format::Metadata;
419	use crate::metadata::format::MetadataInner;
420	use super::*;
421
422
423	// Default impl needed for tests only!
424	impl<S: Default> Default for Manifest<S> {
425		fn default() -> Self {
426			Self { name: Default::default(),
427			       version: Default::default(),
428			       author: Default::default(),
429			       bundle_id: Default::default(),
430			       description: Default::default(),
431			       image_path: Default::default(),
432			       launch_sound_path: Default::default(),
433			       content_warning: Default::default(),
434			       content_warning2: Default::default(),
435			       build_number: Default::default() }
436		}
437	}
438
439
440	#[test]
441	fn manifest_override() {
442		let base = Manifest { name: Some("Name"),
443		                      bundle_id: Some("dev.foo.bar"),
444		                      ..Default::default() };
445
446		let over = Manifest { name: Some("Over"),
447		                      bundle_id: None,
448		                      description: Some("description"),
449		                      ..Default::default() };
450
451
452		{
453			let res = base.override_with(&over);
454			assert_eq!(Some("Over"), res.name());
455			assert_eq!(Some("dev.foo.bar"), res.bundle_id());
456			assert_eq!(Some("description"), res.description());
457		}
458
459		{
460			let res = base.override_with(&over);
461			assert_eq!(Some("Over"), res.name());
462			assert_eq!(Some("dev.foo.bar"), res.bundle_id());
463			assert_eq!(Some("description"), res.description());
464		}
465
466		{
467			let res = base.override_with_extra(&over);
468			assert_eq!(Some("Over"), res.name());
469			assert_eq!(Some("dev.foo.bar"), res.bundle_id());
470			assert_eq!(Some("description"), res.description());
471			assert!(res.iter_extra().is_none());
472		}
473	}
474
475
476	#[test]
477	fn manifest_override_ext() {
478		let base = Manifest { name: Some("Name"),
479		                      bundle_id: Some("dev.foo.bar"),
480		                      ..Default::default() };
481
482		let mut extra = ExtraFields::with_capacity(1);
483		extra.insert("foo".into(), "bar".into());
484
485
486		let base = Ext { main: base, extra };
487
488		let over = Manifest { name: Some("Over"),
489		                      bundle_id: None,
490		                      description: Some("description"),
491		                      ..Default::default() };
492
493
494		{
495			let res = base.override_with(&over);
496			assert_eq!(Some("Over"), res.name());
497			assert_eq!(Some("dev.foo.bar"), res.bundle_id());
498			assert_eq!(Some("description"), res.description());
499		}
500
501		{
502			let res = base.override_with(&over);
503			assert_eq!(Some("Over"), res.name());
504			assert_eq!(Some("dev.foo.bar"), res.bundle_id());
505			assert_eq!(Some("description"), res.description());
506		}
507
508		{
509			let res = base.override_with_extra(&over);
510			assert_eq!(Some("Over"), res.name());
511			assert_eq!(Some("dev.foo.bar"), res.bundle_id());
512			assert_eq!(Some("description"), res.description());
513
514			assert!(res.iter_extra().is_some());
515			let (k, v) = res.iter_extra().unwrap().into_iter().next().unwrap();
516			assert_eq!("foo", k.as_ref());
517			assert_eq!(&ExtraValue::String("bar".into()), v.as_ref());
518		}
519	}
520
521
522	struct CrateInfoNoMeta;
523	impl PackageSource for CrateInfoNoMeta {
524		type Authors = [&'static str];
525		type Metadata = Metadata<String>;
526
527		fn name(&self) -> Cow<str> { "Name".into() }
528		fn authors(&self) -> &Self::Authors { &["John"] }
529		fn version(&self) -> Cow<str> { "0.0.0".into() }
530		fn description(&self) -> Option<Cow<str>> { None }
531		fn bins(&self) -> &[&str] { &[SOME_TARGET] }
532		fn examples(&self) -> &[&str] { &[] }
533		fn metadata(&self) -> Option<&Self::Metadata> { None }
534
535		fn manifest_path(&self) -> Cow<Path> { Cow::Borrowed(Path::new("Cargo.toml")) }
536	}
537
538	#[test]
539	fn manifest_for_base() {
540		let base = CrateInfoNoMeta.manifest_for_crate();
541		let spec = CrateInfoNoMeta.manifest_override_or_crate("target".into(), false);
542		let opt = CrateInfoNoMeta.manifest_override_for("target", false);
543		assert_eq!(opt, Some(spec.to_owned()));
544		assert_eq!(spec, base.into_owned());
545	}
546
547
548	struct CrateInfo(Metadata);
549	impl CrateInfo {
550		fn new() -> Self {
551			let base = Manifest { name: Some("Meta Name"),
552			                      bundle_id: Some("crate.id"),
553			                      ..Default::default() };
554
555			let mut extra = ExtraFields::with_capacity(1);
556			extra.insert("foo".into(), "bar".into());
557			assert!(!extra.is_empty());
558
559			let manifest = Ext { main: base, extra }.into_owned();
560			assert!(manifest.has_extra());
561
562			let bins = {
563				let base = Manifest { name: Some("Bin Name"),
564				                      author: Some("Alex"),
565				                      bundle_id: Some("bin.id"),
566				                      description: Some("description"),
567				                      ..Default::default() };
568
569				let mut extra = ExtraFields::with_capacity(1);
570				extra.insert("boo".into(), 42_usize.into());
571
572
573				let manifest = Ext { main: base, extra }.into_owned();
574				vec![Override { target: SOME_TARGET.to_owned(),
575				                manifest }]
576			};
577
578			let meta = Metadata { inner: MetadataInner { manifest,
579			                                             bins,
580			                                             examples: vec![],
581			                                             assets: Default::default(),
582			                                             dev_assets: Default::default(),
583			                                             options: Default::default(),
584			                                             support: Default::default() } };
585
586			Self(meta)
587		}
588	}
589
590	impl PackageSource for CrateInfo {
591		type Authors = [&'static str];
592		type Metadata = Metadata<String>;
593
594		fn name(&self) -> Cow<str> { "Crate Name".into() }
595		fn authors(&self) -> &[&'static str] { &["John"] }
596		fn version(&self) -> Cow<str> { "0.0.0".into() }
597		fn description(&self) -> Option<Cow<str>> { None }
598
599		fn bins(&self) -> &[&str] { &[SOME_TARGET] }
600		fn examples(&self) -> &[&str] { &[] }
601
602		fn manifest_path(&self) -> Cow<Path> { Cow::Borrowed(Path::new("Cargo.toml")) }
603
604		fn metadata(&self) -> Option<&Self::Metadata> { Some(&self.0) }
605	}
606
607	const SOME_TARGET: &str = "some-target";
608
609	#[test]
610	fn manifest_for_crate() {
611		let base_src = CrateInfo::new();
612		let base = base_src.manifest_for_crate();
613		assert_eq!(Some("Meta Name"), base.name());
614		assert_eq!(Some("John"), base.author());
615		assert_eq!(Some("0.0.0"), base.version());
616		assert_eq!(Some("crate.id"), base.bundle_id());
617		assert!(base.description().is_none());
618		assert!(base.has_extra());
619		let extra = base.iter_extra()
620		                .unwrap()
621		                .into_iter()
622		                .map(|(k, v)| (k.as_ref().to_owned(), v.as_ref().to_owned()))
623		                .collect::<HashMap<_, _>>();
624		assert_eq!(1, extra.len());
625		assert_eq!(Some(&"bar".into()), extra.get("foo"));
626	}
627
628	#[test]
629	fn manifest_for_target_wrong_no_meta() {
630		let spec = CrateInfoNoMeta.manifest_override_or_crate(Some("WRONG"), false);
631
632		assert_eq!(Some("Name"), spec.name());
633		assert_eq!(Some("John"), spec.author());
634		assert_eq!(Some("0.0.0"), spec.version());
635		assert!(spec.bundle_id().is_none());
636	}
637
638	#[test]
639	fn manifest_for_target_wrong() {
640		let base_src = CrateInfo::new();
641		let base = base_src.manifest_for_crate();
642		let spec = base_src.manifest_override_or_crate(Some("WRONG"), false);
643		assert_eq!(Some("Meta Name"), spec.name());
644		assert_eq!(Some("John"), spec.author());
645		assert_eq!(Some("0.0.0"), spec.version());
646		assert_eq!(Some("crate.id"), spec.bundle_id());
647		assert_eq!(spec, base.into_owned());
648	}
649
650	#[test]
651	fn manifest_for_target_bin() {
652		let base_src = CrateInfo::new();
653		let spec = base_src.manifest_override_or_crate(SOME_TARGET.into(), false);
654		assert_eq!(Some("Bin Name"), spec.name());
655		assert_eq!(Some("Alex"), spec.author());
656		assert_eq!(Some("0.0.0"), spec.version());
657		assert_eq!(Some("bin.id"), spec.bundle_id());
658		assert_eq!(Some("description"), spec.description());
659		let extra = spec.iter_extra()
660		                .unwrap()
661		                .into_iter()
662		                .map(|(k, v)| (k.as_ref().to_owned(), v.as_ref().to_owned()))
663		                .collect::<HashMap<_, _>>();
664		assert_eq!(2, extra.len());
665		assert_eq!(Some(&"bar".into()), extra.get("foo"));
666		assert_eq!(Some(&42_usize.into()), extra.get("boo"));
667	}
668}