use serde::{Deserialize, Serialize};
use crate::model::{Profile, ProjectId, VarId};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ManifestSnapshot {
pub project_id: ProjectId,
pub name: String,
pub bindings: Vec<ManifestBinding>,
}
impl ManifestSnapshot {
#[must_use]
pub fn effective_bindings(&self, profile: &Profile) -> Vec<&ManifestBinding> {
let mut by_key: std::collections::BTreeMap<&str, &ManifestBinding> =
std::collections::BTreeMap::new();
for b in &self.bindings {
if b.profile.is_default() {
by_key.insert(b.key.as_str(), b);
}
}
for b in &self.bindings {
if b.profile == *profile {
by_key.insert(b.key.as_str(), b);
}
}
by_key.into_values().collect()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ManifestBinding {
pub key: String,
pub source: BindingSource,
pub profile: Profile,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum BindingSource {
Registry {
var_id: VarId,
},
Inline {
value: String,
},
}
#[cfg(test)]
mod tests {
use super::*;
fn binding(key: &str, profile: Profile, value: &str) -> ManifestBinding {
ManifestBinding {
key: key.to_owned(),
source: BindingSource::Inline {
value: value.to_owned(),
},
profile,
}
}
#[test]
fn effective_default_profile_keeps_defaults() {
let snap = ManifestSnapshot {
project_id: ProjectId::new_v4(),
name: "x".into(),
bindings: vec![
binding("FOO", Profile::default_profile(), "1"),
binding("BAR", Profile::default_profile(), "2"),
],
};
let effective = snap.effective_bindings(&Profile::default_profile());
assert_eq!(effective.len(), 2);
}
#[test]
fn named_profile_overrides_default_on_key() {
let snap = ManifestSnapshot {
project_id: ProjectId::new_v4(),
name: "x".into(),
bindings: vec![
binding("FOO", Profile::default_profile(), "default"),
binding("FOO", Profile::named("dev"), "dev"),
binding("BAR", Profile::default_profile(), "common"),
],
};
let dev = snap.effective_bindings(&Profile::named("dev"));
assert_eq!(dev.len(), 2);
let foo = dev.iter().find(|b| b.key == "FOO").expect("FOO present");
match &foo.source {
BindingSource::Inline { value } => assert_eq!(value, "dev"),
BindingSource::Registry { .. } => panic!("unexpected source"),
}
}
}