1use std::{borrow::Cow, collections::BTreeMap};
6
7use serde::{Deserialize, Serialize};
8use strum_macros::{EnumCount, VariantArray};
9
10mod package_formats;
11#[doc(inline)]
12pub use package_formats::*;
13
14#[derive(Clone, Debug, Serialize, Deserialize)]
18#[serde(rename_all = "kebab-case")]
19pub struct Meta {
20 pub binstall: Option<PkgMeta>,
21}
22
23#[derive(
25 Debug,
26 Copy,
27 Clone,
28 Eq,
29 PartialEq,
30 Ord,
31 PartialOrd,
32 EnumCount,
33 VariantArray,
34 Deserialize,
35 Serialize,
36)]
37#[serde(rename_all = "kebab-case")]
38pub enum Strategy {
39 CrateMetaData,
42 QuickInstall,
44 Compile,
46}
47
48impl Strategy {
49 pub const fn to_str(self) -> &'static str {
50 match self {
51 Strategy::CrateMetaData => "crate-meta-data",
52 Strategy::QuickInstall => "quick-install",
53 Strategy::Compile => "compile",
54 }
55 }
56}
57
58#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
62#[serde(rename_all = "kebab-case", default)]
63pub struct PkgMeta {
64 pub pkg_url: Option<String>,
66
67 pub pkg_fmt: Option<PkgFmt>,
69
70 pub bin_dir: Option<String>,
72
73 pub signing: Option<PkgSigning>,
75
76 pub disabled_strategies: Option<Box<[Strategy]>>,
78
79 pub overrides: BTreeMap<String, PkgOverride>,
81}
82
83impl PkgMeta {
84 pub fn merge(&mut self, pkg_override: &PkgOverride) {
86 if let Some(o) = &pkg_override.pkg_url {
87 self.pkg_url = Some(o.clone());
88 }
89 if let Some(o) = &pkg_override.pkg_fmt {
90 self.pkg_fmt = Some(*o);
91 }
92 if let Some(o) = &pkg_override.bin_dir {
93 self.bin_dir = Some(o.clone());
94 }
95 }
96
97 pub fn merge_overrides<'a, It>(&self, pkg_overrides: It) -> Self
101 where
102 It: IntoIterator<Item = &'a PkgOverride> + Clone,
103 {
104 let ignore_disabled_strategies = pkg_overrides
105 .clone()
106 .into_iter()
107 .any(|pkg_override| pkg_override.ignore_disabled_strategies);
108
109 Self {
110 pkg_url: pkg_overrides
111 .clone()
112 .into_iter()
113 .find_map(|pkg_override| pkg_override.pkg_url.clone())
114 .or_else(|| self.pkg_url.clone()),
115
116 pkg_fmt: pkg_overrides
117 .clone()
118 .into_iter()
119 .find_map(|pkg_override| pkg_override.pkg_fmt)
120 .or(self.pkg_fmt),
121
122 bin_dir: pkg_overrides
123 .clone()
124 .into_iter()
125 .find_map(|pkg_override| pkg_override.bin_dir.clone())
126 .or_else(|| self.bin_dir.clone()),
127
128 signing: pkg_overrides
129 .clone()
130 .into_iter()
131 .find_map(|pkg_override| pkg_override.signing.clone())
132 .or_else(|| self.signing.clone()),
133
134 disabled_strategies: if ignore_disabled_strategies {
135 None
136 } else {
137 let mut disabled_strategies = pkg_overrides
138 .into_iter()
139 .filter_map(|pkg_override| pkg_override.disabled_strategies.as_deref())
140 .flatten()
141 .chain(self.disabled_strategies.as_deref().into_iter().flatten())
142 .copied()
143 .collect::<Vec<Strategy>>();
144
145 disabled_strategies.sort_unstable();
146 disabled_strategies.dedup();
147
148 Some(disabled_strategies.into_boxed_slice())
149 },
150
151 overrides: Default::default(),
152 }
153 }
154}
155
156#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
160#[serde(rename_all = "kebab-case", default)]
161pub struct PkgOverride {
162 pub pkg_url: Option<String>,
164
165 pub pkg_fmt: Option<PkgFmt>,
167
168 pub bin_dir: Option<String>,
170
171 pub disabled_strategies: Option<Box<[Strategy]>>,
173
174 pub signing: Option<PkgSigning>,
176
177 #[serde(skip)]
178 pub ignore_disabled_strategies: bool,
179}
180
181#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
182#[serde(rename_all = "kebab-case")]
183pub struct BinMeta {
184 pub name: String,
186
187 pub path: String,
189}
190
191#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
192#[serde(rename_all = "kebab-case")]
193pub struct PkgSigning {
194 pub algorithm: SigningAlgorithm,
196
197 pub pubkey: Cow<'static, str>,
199
200 #[serde(default)]
202 pub file: Option<String>,
203}
204
205#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
206#[serde(rename_all = "kebab-case")]
207#[non_exhaustive]
208pub enum SigningAlgorithm {
209 Minisign,
211}
212
213#[cfg(test)]
214mod tests {
215 use strum::VariantArray;
216
217 use super::*;
218
219 #[test]
220 fn test_strategy_ser() {
221 Strategy::VARIANTS.iter().for_each(|strategy| {
222 assert_eq!(
223 serde_json::to_string(&strategy).unwrap(),
224 format!(r#""{}""#, strategy.to_str())
225 )
226 });
227 }
228}