use crate::resource::ResourceKind;
use similar::{ChangeTag, TextDiff};
pub mod catalog;
pub mod content_block;
pub mod custom_attribute;
pub mod email_template;
pub mod orphan;
#[derive(Debug, Clone)]
pub struct TextDiffSummary {
pub additions: usize,
pub deletions: usize,
}
pub(crate) fn compute_text_diff(from: &str, to: &str) -> TextDiffSummary {
let diff = TextDiff::from_lines(from, to);
let mut additions = 0;
let mut deletions = 0;
for change in diff.iter_all_changes() {
match change.tag() {
ChangeTag::Insert => additions += 1,
ChangeTag::Delete => deletions += 1,
ChangeTag::Equal => {}
}
}
TextDiffSummary {
additions,
deletions,
}
}
pub(crate) fn opt_str_eq(a: &Option<String>, b: &Option<String>) -> bool {
a.as_deref().unwrap_or("") == b.as_deref().unwrap_or("")
}
pub(crate) fn tags_eq_unordered(a: &[String], b: &[String]) -> bool {
if a.len() != b.len() {
return false;
}
let mut a: Vec<&str> = a.iter().map(String::as_str).collect();
let mut b: Vec<&str> = b.iter().map(String::as_str).collect();
a.sort_unstable();
b.sort_unstable();
a == b
}
#[derive(Debug, Clone, PartialEq)]
pub enum DiffOp<T> {
Added(T),
Removed(T),
Modified { from: T, to: T },
Unchanged,
}
impl<T> DiffOp<T> {
pub fn is_change(&self) -> bool {
!matches!(self, Self::Unchanged)
}
pub fn is_destructive(&self) -> bool {
matches!(self, Self::Removed(_))
}
}
#[derive(Debug, Clone)]
pub enum ResourceDiff {
CatalogSchema(catalog::CatalogSchemaDiff),
ContentBlock(content_block::ContentBlockDiff),
EmailTemplate(email_template::EmailTemplateDiff),
CustomAttribute(custom_attribute::CustomAttributeDiff),
}
impl ResourceDiff {
pub fn kind(&self) -> ResourceKind {
match self {
Self::CatalogSchema(_) => ResourceKind::CatalogSchema,
Self::ContentBlock(_) => ResourceKind::ContentBlock,
Self::EmailTemplate(_) => ResourceKind::EmailTemplate,
Self::CustomAttribute(_) => ResourceKind::CustomAttribute,
}
}
pub fn name(&self) -> &str {
match self {
Self::CatalogSchema(d) => &d.name,
Self::ContentBlock(d) => &d.name,
Self::EmailTemplate(d) => &d.name,
Self::CustomAttribute(d) => &d.name,
}
}
pub fn has_changes(&self) -> bool {
match self {
Self::CatalogSchema(d) => d.has_changes(),
Self::ContentBlock(d) => d.has_changes(),
Self::EmailTemplate(d) => d.has_changes(),
Self::CustomAttribute(d) => d.has_changes(),
}
}
pub fn is_actionable(&self) -> bool {
match self {
Self::CustomAttribute(d) => d.is_actionable(),
other => other.has_changes(),
}
}
pub fn has_destructive(&self) -> bool {
match self {
Self::CatalogSchema(d) => d.has_destructive(),
Self::ContentBlock(_) => false,
Self::EmailTemplate(_) => false,
Self::CustomAttribute(_) => false,
}
}
pub fn is_orphan(&self) -> bool {
match self {
Self::ContentBlock(d) => d.is_orphan(),
Self::EmailTemplate(d) => d.is_orphan(),
_ => false,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct DiffSummary {
pub diffs: Vec<ResourceDiff>,
}
impl DiffSummary {
pub fn changed_count(&self) -> usize {
self.diffs.iter().filter(|d| d.has_changes()).count()
}
pub fn actionable_count(&self) -> usize {
self.diffs.iter().filter(|d| d.is_actionable()).count()
}
pub fn destructive_count(&self) -> usize {
self.diffs.iter().filter(|d| d.has_destructive()).count()
}
pub fn orphan_count(&self) -> usize {
self.diffs.iter().filter(|d| d.is_orphan()).count()
}
pub fn in_sync_count(&self) -> usize {
self.diffs.iter().filter(|d| !d.has_changes()).count()
}
}