use chrono::{DateTime, Utc};
use crate::model::DumpStatus;
pub struct DumpOutcome {
pub path: std::path::PathBuf,
pub bytes: u64,
pub cleaned_up: bool,
pub reused: bool,
pub created_at: Option<DateTime<Utc>>,
}
pub struct DumpDeleteOutcome {
pub deleted: bool,
pub note: Option<String>,
}
pub enum DumpEvent {
Triggered {
id: String,
},
Polling {
elapsed_secs: u64,
status: DumpStatus,
},
Downloading,
Done {
bytes: u64,
},
Adopting {
id: String,
},
Deleting {
id: String,
},
ProbeCreated {
id: String,
},
DiscardingOtherProjectDump {
id: String,
project_iri: String,
},
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dump_outcome_construction() {
let outcome = DumpOutcome {
path: std::path::PathBuf::from("./0001-20260529T120000Z.zip"),
bytes: 1024,
cleaned_up: true,
reused: false,
created_at: None,
};
assert_eq!(outcome.bytes, 1024);
assert!(outcome.cleaned_up);
assert!(!outcome.reused);
assert!(outcome.created_at.is_none());
assert_eq!(
outcome.path,
std::path::PathBuf::from("./0001-20260529T120000Z.zip")
);
}
#[test]
fn dump_outcome_reused_with_created_at() {
use chrono::TimeZone;
let ts = Utc.with_ymd_and_hms(2026, 5, 20, 14, 3, 0).unwrap();
let outcome = DumpOutcome {
path: std::path::PathBuf::from("./0001-20260529T120000Z.zip"),
bytes: 999,
cleaned_up: false,
reused: true,
created_at: Some(ts),
};
assert!(outcome.reused);
assert_eq!(outcome.created_at, Some(ts));
}
#[test]
fn dump_delete_outcome_construction() {
let deleted = DumpDeleteOutcome {
deleted: true,
note: None,
};
assert!(deleted.deleted);
assert!(deleted.note.is_none());
let probe = DumpDeleteOutcome {
deleted: false,
note: Some("no dump existed; a probe created an in-progress dump probe-id-42 that will complete server-side".to_string()),
};
assert!(!probe.deleted);
assert!(probe.note.is_some());
assert!(probe.note.unwrap().contains("probe-id-42"));
let foreign_slot = DumpDeleteOutcome {
deleted: false,
note: Some(
"no dump for the requested project to delete; the server's single dump slot is held by a different project (http://rdfh.ch/projects/0002)".to_string(),
),
};
assert!(!foreign_slot.deleted);
let note = foreign_slot.note.unwrap();
assert!(note.contains("0002"), "note must name the foreign project");
}
#[test]
fn dump_event_variants_construct() {
let triggered = DumpEvent::Triggered {
id: "dump-id-42".into(),
};
let polling = DumpEvent::Polling {
elapsed_secs: 0,
status: DumpStatus::InProgress,
};
let downloading = DumpEvent::Downloading;
let done = DumpEvent::Done { bytes: 2048 };
let adopting = DumpEvent::Adopting {
id: "existing-id".into(),
};
let deleting = DumpEvent::Deleting {
id: "del-id".into(),
};
let probe_created = DumpEvent::ProbeCreated {
id: "probe-id".into(),
};
let discarding_other = DumpEvent::DiscardingOtherProjectDump {
id: "foreign-id".into(),
project_iri: "http://rdfh.ch/projects/0002".into(),
};
match triggered {
DumpEvent::Triggered { id } => assert_eq!(id, "dump-id-42"),
_ => panic!("unexpected variant"),
}
match polling {
DumpEvent::Polling {
elapsed_secs,
status,
} => {
assert_eq!(elapsed_secs, 0);
assert_eq!(status, DumpStatus::InProgress);
}
_ => panic!("unexpected variant"),
}
assert!(matches!(downloading, DumpEvent::Downloading));
match done {
DumpEvent::Done { bytes } => assert_eq!(bytes, 2048),
_ => panic!("unexpected variant"),
}
match adopting {
DumpEvent::Adopting { id } => assert_eq!(id, "existing-id"),
_ => panic!("unexpected variant"),
}
match deleting {
DumpEvent::Deleting { id } => assert_eq!(id, "del-id"),
_ => panic!("unexpected variant"),
}
match probe_created {
DumpEvent::ProbeCreated { id } => assert_eq!(id, "probe-id"),
_ => panic!("unexpected variant"),
}
match discarding_other {
DumpEvent::DiscardingOtherProjectDump { id, project_iri } => {
assert_eq!(id, "foreign-id");
assert_eq!(project_iri, "http://rdfh.ch/projects/0002");
}
_ => panic!("unexpected variant"),
}
}
}