evault_core/model/
manifest.rs1use serde::{Deserialize, Serialize};
9
10use crate::model::{Profile, ProjectId, VarId};
11
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
18pub struct ManifestSnapshot {
19 pub project_id: ProjectId,
21
22 pub name: String,
24
25 pub bindings: Vec<ManifestBinding>,
27}
28
29impl ManifestSnapshot {
30 #[must_use]
35 pub fn effective_bindings(&self, profile: &Profile) -> Vec<&ManifestBinding> {
36 let mut by_key: std::collections::BTreeMap<&str, &ManifestBinding> =
37 std::collections::BTreeMap::new();
38 for b in &self.bindings {
40 if b.profile.is_default() {
41 by_key.insert(b.key.as_str(), b);
42 }
43 }
44 for b in &self.bindings {
46 if b.profile == *profile {
47 by_key.insert(b.key.as_str(), b);
48 }
49 }
50 by_key.into_values().collect()
51 }
52}
53
54#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
56pub struct ManifestBinding {
57 pub key: String,
59
60 pub source: BindingSource,
62
63 pub profile: Profile,
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
69#[serde(tag = "kind", rename_all = "snake_case")]
70pub enum BindingSource {
71 Registry {
73 var_id: VarId,
75 },
76 Inline {
78 value: String,
80 },
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 fn binding(key: &str, profile: Profile, value: &str) -> ManifestBinding {
88 ManifestBinding {
89 key: key.to_owned(),
90 source: BindingSource::Inline {
91 value: value.to_owned(),
92 },
93 profile,
94 }
95 }
96
97 #[test]
98 fn effective_default_profile_keeps_defaults() {
99 let snap = ManifestSnapshot {
100 project_id: ProjectId::new_v4(),
101 name: "x".into(),
102 bindings: vec![
103 binding("FOO", Profile::default_profile(), "1"),
104 binding("BAR", Profile::default_profile(), "2"),
105 ],
106 };
107 let effective = snap.effective_bindings(&Profile::default_profile());
108 assert_eq!(effective.len(), 2);
109 }
110
111 #[test]
112 fn named_profile_overrides_default_on_key() {
113 let snap = ManifestSnapshot {
114 project_id: ProjectId::new_v4(),
115 name: "x".into(),
116 bindings: vec![
117 binding("FOO", Profile::default_profile(), "default"),
118 binding("FOO", Profile::named("dev"), "dev"),
119 binding("BAR", Profile::default_profile(), "common"),
120 ],
121 };
122 let dev = snap.effective_bindings(&Profile::named("dev"));
123 assert_eq!(dev.len(), 2);
125 let foo = dev.iter().find(|b| b.key == "FOO").expect("FOO present");
126 match &foo.source {
127 BindingSource::Inline { value } => assert_eq!(value, "dev"),
128 BindingSource::Registry { .. } => panic!("unexpected source"),
129 }
130 }
131}