1use serde::Deserialize;
8use serde_json::Value;
9
10const RAW: &str = include_str!("../../secret-templates.json");
11
12#[derive(Debug, Clone, Deserialize)]
13pub struct Template {
14 pub id: String,
15 pub name: String,
16 pub icon: String,
17 pub category: String,
18 #[serde(rename = "secretType")]
19 pub secret_type: String,
20 pub defaults: serde_json::Map<String, Value>,
22 #[serde(rename = "requiredFields", default)]
23 pub required_fields: Vec<String>,
24 #[serde(default)]
25 pub hints: serde_json::Map<String, Value>,
26}
27
28#[derive(Deserialize)]
29struct File {
30 templates: Vec<Template>,
31}
32
33pub fn list() -> Vec<Template> {
34 serde_json::from_str::<File>(RAW)
35 .expect("secret-templates.json failed to parse: a build-time defect")
36 .templates
37}
38
39pub fn find(id: &str) -> Option<Template> {
40 list().into_iter().find(|t| t.id.eq_ignore_ascii_case(id))
41}
42
43#[cfg(test)]
44mod tests {
45 use super::*;
46
47 #[test]
48 fn every_template_parses_and_has_a_known_type() {
49 let all = list();
50 assert!(all.len() >= 10);
51 for t in &all {
52 assert!(
53 crate::secret_types::find(&t.secret_type).is_some(),
54 "{}: unknown secretType {}",
55 t.id,
56 t.secret_type
57 );
58 assert!(!t.defaults.is_empty(), "{} pre-fills nothing", t.id);
59 }
60 let mut ids: Vec<_> = all.iter().map(|t| t.id.clone()).collect();
61 ids.sort();
62 ids.dedup();
63 assert_eq!(ids.len(), all.len(), "duplicate template id");
64 }
65
66 #[test]
67 fn find_is_case_insensitive() {
68 assert_eq!(find("GitHub-PAT").unwrap().id, "github-pat");
69 assert!(find("nope").is_none());
70 }
71}