use crate::context::Context;
use crate::{PublishEvidence, PublisherGroup};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PreflightCheck {
Pass,
Warning(String),
Blocker(String),
}
pub trait Publisher: Send + Sync {
fn name(&self) -> &str;
fn run(&self, ctx: &mut Context) -> anyhow::Result<PublishEvidence>;
fn group(&self) -> PublisherGroup;
fn required(&self) -> bool;
fn rollback(&self, _ctx: &mut Context, _evidence: &PublishEvidence) -> anyhow::Result<()> {
Ok(())
}
fn preflight(&self, _ctx: &Context) -> anyhow::Result<PreflightCheck> {
Ok(PreflightCheck::Pass)
}
fn rollback_scope_needed(&self) -> Option<&'static str> {
None
}
}
pub fn rollback_empty_warning_msg(publisher: &str, target_label: &str) -> String {
format!(
"{}: no {} recorded in evidence; verify {} state manually",
publisher, target_label, publisher
)
}
#[cfg(test)]
mod tests {
use super::*;
struct MinimalPublisher;
impl Publisher for MinimalPublisher {
fn name(&self) -> &str {
"minimal"
}
fn run(&self, _ctx: &mut Context) -> anyhow::Result<PublishEvidence> {
Ok(PublishEvidence::new("minimal"))
}
fn group(&self) -> PublisherGroup {
PublisherGroup::Manager
}
fn required(&self) -> bool {
false
}
}
#[test]
fn rollback_default_is_noop_ok() {
let p = MinimalPublisher;
let mut ctx = Context::test_fixture();
let evidence = PublishEvidence::new("minimal");
assert!(p.rollback(&mut ctx, &evidence).is_ok());
}
#[test]
fn preflight_default_is_pass() {
let p = MinimalPublisher;
let ctx = Context::test_fixture();
assert!(matches!(p.preflight(&ctx).unwrap(), PreflightCheck::Pass));
}
#[test]
fn rollback_scope_needed_default_is_none() {
let p = MinimalPublisher;
assert!(p.rollback_scope_needed().is_none());
}
#[test]
fn pending_outcome_round_trips_through_context() {
let mut ctx = Context::test_fixture();
assert!(ctx.take_pending_outcome().is_none());
ctx.record_publisher_outcome(crate::PublisherOutcome::PendingModeration);
assert!(matches!(
ctx.take_pending_outcome(),
Some(crate::PublisherOutcome::PendingModeration)
));
assert!(
ctx.take_pending_outcome().is_none(),
"slot must be empty after take"
);
ctx.record_publisher_outcome(crate::PublisherOutcome::PendingModeration);
ctx.record_publisher_outcome(crate::PublisherOutcome::PendingValidation);
assert!(matches!(
ctx.take_pending_outcome(),
Some(crate::PublisherOutcome::PendingValidation)
));
}
}