pop_common/templates/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// SPDX-License-Identifier: GPL-3.0

use strum::{EnumMessage, EnumProperty, VariantArray};
pub use thiserror::Error;

pub mod extractor;

#[derive(Error, Debug)]
pub enum Error {
	#[error("The `Repository` property is missing from the template variant")]
	RepositoryMissing,

	#[error("The `TypeMissing` property is missing from the template variant")]
	TypeMissing,
}

/// A trait for templates. A template is a variant of a template type.
pub trait Template:
	Clone + Default + EnumMessage + EnumProperty + Eq + PartialEq + VariantArray
{
	// What is the template's type strum property identifier.
	const PROPERTY: &'static str = "Type";

	/// Get the template's name.
	fn name(&self) -> &str {
		self.get_message().unwrap_or_default()
	}

	/// Get the description of the template.
	fn description(&self) -> &str {
		self.get_detailed_message().unwrap_or_default()
	}

	/// Get the template's repository url.
	fn repository_url(&self) -> Result<&str, Error> {
		self.get_str("Repository").ok_or(Error::RepositoryMissing)
	}

	/// Get the list of supported templates.
	fn templates() -> &'static [Self] {
		Self::VARIANTS
	}

	/// Get the type of the template.
	fn template_type(&self) -> Result<&str, Error> {
		self.get_str(Self::PROPERTY).ok_or(Error::TypeMissing)
	}
}

/// A trait for defining overarching types of specific template variants.
/// A Type has many Template variants.
/// The method `default_template` should be implemented unless
/// no default templates are desired.
pub trait Type<T: Template>: Clone + Default + EnumMessage + Eq + PartialEq + VariantArray {
	/// Get the list of types supported.
	fn types() -> &'static [Self] {
		Self::VARIANTS
	}

	/// Get types's name.
	fn name(&self) -> &str {
		self.get_message().unwrap_or_default()
	}

	/// Get the default template of the type.
	fn default_template(&self) -> Option<T> {
		None
	}

	/// Get the type's description.
	fn description(&self) -> &str {
		self.get_detailed_message().unwrap_or_default()
	}

	/// Get the list of templates of the type.
	fn templates(&self) -> Vec<&T> {
		T::VARIANTS
			.iter()
			.filter(|t| t.get_str(T::PROPERTY) == Some(self.name()))
			.collect()
	}

	/// Check the type provides the template.
	fn provides(&self, template: &T) -> bool {
		// Match explicitly on type name (message)
		template.get_str(T::PROPERTY) == Some(self.name())
	}
}

#[macro_export]
macro_rules! enum_variants {
	($e: ty) => {{
		PossibleValuesParser::new(
			<$e>::VARIANTS
				.iter()
				.map(|p| PossibleValue::new(p.as_ref()))
				.collect::<Vec<_>>(),
		)
		.try_map(|s| <$e>::from_str(&s).map_err(|e| format!("could not convert from {s} to type")))
	}};
}