use chrono::{DateTime, Utc};
use faultline_core::{ChangeEvent, DatasetSnapshot};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct DiffRequest {
pub left: String,
pub right: String,
pub layer: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DiffSummary {
pub diff_id: Uuid,
pub added: u64,
pub removed: u64,
pub changed: u64,
}
impl DiffSummary {
pub fn empty() -> Self {
Self {
diff_id: Uuid::new_v4(),
added: 0,
removed: 0,
changed: 0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum DiffClassification {
Deterministic,
NoChanges,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct SnapshotState {
pub snapshot: DatasetSnapshot,
pub features: Vec<faultline_core::FeatureRef>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DiffArtifact {
pub artifact_id: Uuid,
pub generated_at: DateTime<Utc>,
pub classification: DiffClassification,
pub left_snapshot_id: Uuid,
pub right_snapshot_id: Uuid,
pub summary: DiffSummary,
pub change_events: Vec<ChangeEvent>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn summary_empty_starts_at_zero() {
let summary = DiffSummary::empty();
assert_eq!(summary.added, 0);
assert_eq!(summary.removed, 0);
assert_eq!(summary.changed, 0);
}
#[test]
fn diff_artifact_round_trip_serialization() {
let snapshot = DatasetSnapshot::new("roads");
let summary = DiffSummary::empty();
let artifact = DiffArtifact {
artifact_id: Uuid::new_v4(),
generated_at: Utc::now(),
classification: DiffClassification::NoChanges,
left_snapshot_id: snapshot.snapshot_id,
right_snapshot_id: snapshot.snapshot_id,
summary,
change_events: Vec::new(),
};
let json = serde_json::to_string(&artifact).expect("serialize artifact");
let restored: DiffArtifact = serde_json::from_str(&json).expect("deserialize artifact");
assert_eq!(restored.classification, DiffClassification::NoChanges);
assert!(restored.change_events.is_empty());
}
}