pop_common/templates/
mod.rs

1// SPDX-License-Identifier: GPL-3.0
2
3use strum::{EnumMessage, EnumProperty, VariantArray};
4pub use thiserror::Error;
5
6/// Functions for extracting a template's files.
7pub mod extractor;
8
9/// An error relating to templates or template variants.
10#[derive(Error, Debug)]
11pub enum Error {
12	/// The `Repository` property is missing from the template variant.
13	#[error("The `Repository` property is missing from the template variant")]
14	RepositoryMissing,
15	/// The `TypeMissing` property is missing from the template variant.
16	#[error("The `TypeMissing` property is missing from the template variant")]
17	TypeMissing,
18}
19
20/// A trait for templates. A template is a variant of a template type.
21pub trait Template:
22	Clone + Default + EnumMessage + EnumProperty + Eq + PartialEq + VariantArray
23{
24	/// The template's type property identifier.
25	const PROPERTY: &'static str = "Type";
26
27	/// Get the template's name.
28	fn name(&self) -> &str {
29		self.get_message().unwrap_or_default()
30	}
31
32	/// Get the description of the template.
33	fn description(&self) -> &str {
34		self.get_detailed_message().unwrap_or_default()
35	}
36
37	/// Get the template's repository url.
38	fn repository_url(&self) -> Result<&str, Error> {
39		self.get_str("Repository").ok_or(Error::RepositoryMissing)
40	}
41
42	/// Get the list of supported templates.
43	fn templates() -> &'static [Self] {
44		Self::VARIANTS
45	}
46
47	/// Get the type of the template.
48	fn template_type(&self) -> Result<&str, Error> {
49		self.get_str(Self::PROPERTY).ok_or(Error::TypeMissing)
50	}
51
52	/// Get whether the template is deprecated.
53	fn is_deprecated(&self) -> bool {
54		self.get_str("IsDeprecated") == Some("true")
55	}
56
57	/// Get the deprecation message for the template
58	fn deprecated_message(&self) -> &str {
59		self.get_str("DeprecatedMessage").unwrap_or_default()
60	}
61}
62
63/// A trait for defining overarching types of specific template variants.
64/// A Type has many Template variants.
65/// The method `default_template` should be implemented unless
66/// no default templates are desired.
67pub trait Type<T: Template>: Clone + Default + EnumMessage + Eq + PartialEq + VariantArray {
68	/// Get the list of types supported.
69	fn types() -> &'static [Self] {
70		Self::VARIANTS
71	}
72
73	/// Get types's name.
74	fn name(&self) -> &str {
75		self.get_message().unwrap_or_default()
76	}
77
78	/// Get the default template of the type.
79	fn default_template(&self) -> Option<T> {
80		None
81	}
82
83	/// Get the type's description.
84	fn description(&self) -> &str {
85		self.get_detailed_message().unwrap_or_default()
86	}
87
88	/// Get the list of templates of the type.
89	fn templates(&self) -> Vec<&T> {
90		T::VARIANTS
91			.iter()
92			.filter(|t| t.get_str(T::PROPERTY) == Some(self.name()) && !t.is_deprecated())
93			.collect()
94	}
95
96	/// Check the type provides the template.
97	fn provides(&self, template: &T) -> bool {
98		// Match explicitly on type name (message)
99		template.get_str(T::PROPERTY) == Some(self.name())
100	}
101}
102
103/// The possible values from the variants of an enum.
104#[macro_export]
105macro_rules! enum_variants {
106	($e: ty) => {{
107		PossibleValuesParser::new(
108			<$e>::VARIANTS
109				.iter()
110				.map(|p| PossibleValue::new(p.as_ref()))
111				.collect::<Vec<_>>(),
112		)
113		.try_map(|s| <$e>::from_str(&s).map_err(|e| format!("could not convert from {s} to type")))
114	}};
115}
116
117/// The possible values from the variants of an enum which are not deprecated.
118#[macro_export]
119macro_rules! enum_variants_without_deprecated {
120	($e:ty) => {{
121		<$e>::VARIANTS
122			.iter()
123			.filter(|variant| !variant.is_deprecated()) // Exclude deprecated variants for --help
124			.map(|v| v.as_ref())
125			.collect::<Vec<_>>()
126			.join(", ")
127	}};
128}