use std::collections::BTreeSet;
use crate::config::Config;
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ManifestDiff {
pub added: BTreeSet<String>,
pub removed: BTreeSet<String>,
pub changed: BTreeSet<String>,
}
impl ManifestDiff {
pub fn compute(old: &Config, new: &Config) -> Self {
let mut diff = ManifestDiff::default();
for name in new.services.keys() {
match old.services.get(name) {
None => {
diff.added.insert(name.clone());
}
Some(old_svc) => {
let new_svc = &new.services[name];
if old_svc.compute_hash() != new_svc.compute_hash() {
diff.changed.insert(name.clone());
}
}
}
}
for name in old.services.keys() {
if !new.services.contains_key(name) {
diff.removed.insert(name.clone());
}
}
diff
}
pub fn is_empty(&self) -> bool {
self.added.is_empty() && self.removed.is_empty() && self.changed.is_empty()
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use super::*;
use crate::config::{Config, ProjectConfig, ServiceConfig, Version};
fn svc(command: &str) -> ServiceConfig {
ServiceConfig {
command: command.to_string(),
..ServiceConfig::default()
}
}
fn config(services: &[(&str, &str)]) -> Config {
Config {
version: Version::V2,
project: ProjectConfig::default(),
services: services
.iter()
.map(|(name, cmd)| (name.to_string(), svc(cmd)))
.collect::<HashMap<_, _>>(),
project_dir: None,
env: None,
metrics: Default::default(),
logs: Default::default(),
status: Default::default(),
}
}
#[test]
fn identical_configs_diff_to_nothing() {
let a = config(&[("web", "run"), ("api", "serve")]);
let b = config(&[("web", "run"), ("api", "serve")]);
assert!(ManifestDiff::compute(&a, &b).is_empty());
}
#[test]
fn added_removed_and_changed_are_classified() {
let old = config(&[("web", "run"), ("api", "serve"), ("gone", "x")]);
let new = config(&[("web", "run"), ("api", "serve-v2"), ("fresh", "y")]);
let diff = ManifestDiff::compute(&old, &new);
assert_eq!(diff.added, BTreeSet::from(["fresh".to_string()]));
assert_eq!(diff.removed, BTreeSet::from(["gone".to_string()]));
assert_eq!(diff.changed, BTreeSet::from(["api".to_string()]));
}
#[test]
fn unchanged_service_is_not_in_any_set() {
let old = config(&[("web", "run")]);
let new = config(&[("web", "run"), ("worker", "loop")]);
let diff = ManifestDiff::compute(&old, &new);
assert!(!diff.changed.contains("web"));
assert!(!diff.removed.contains("web"));
assert_eq!(diff.added, BTreeSet::from(["worker".to_string()]));
}
}