use super::{LinkRefusal, RunError};
use crate::{Command, Operation};
use chrono::{DateTime, Utc};
use dpm_model::{ActorId, LineageId, OperationId, RunRecord, WorkItemId, WorkspaceId};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OperationFacts {
pub id: OperationId,
pub actor: ActorId,
pub timestamp: DateTime<Utc>,
pub work: Option<WorkItemId>,
pub workspace: WorkspaceId,
pub lineage: LineageId,
}
impl OperationFacts {
#[must_use]
pub fn of(operation: &Operation, workspace: WorkspaceId, lineage: LineageId) -> Self {
Self {
id: operation.id,
actor: operation.actor.clone(),
timestamp: operation.timestamp,
work: command_work(&operation.command),
workspace,
lineage,
}
}
}
fn command_work(command: &Command) -> Option<WorkItemId> {
match command {
Command::RatifyContract { work }
| Command::Reject { work, .. }
| Command::Claim { work }
| Command::Release { work, .. }
| Command::Handoff { work, .. }
| Command::Start { work, .. }
| Command::Block { work, .. }
| Command::Unblock { work }
| Command::ReportProgress { work, .. }
| Command::Submit { work, .. }
| Command::Verify { work, .. }
| Command::AttachArtifact { work, .. }
| Command::UnlinkExternal { work, .. }
| Command::RevalidateBasis { work, .. } => Some(*work),
Command::LinkExternal(request) => Some(request.work),
Command::ApplyChange { .. }
| Command::RevertChange { .. }
| Command::WaiveDependency { .. }
| Command::RestoreDependency { .. }
| Command::Decide { .. } => None,
}
}
pub fn check_link(
record: &RunRecord,
ended_at: Option<DateTime<Utc>>,
operation: &OperationFacts,
) -> Result<(), RunError> {
let refuse = |reason| {
Err(RunError::LinkRefused {
run: record.id,
operation: operation.id,
reason,
})
};
if operation.workspace != record.contract.workspace_id {
return refuse(LinkRefusal::OtherWorkspace);
}
if operation.lineage != record.contract.lineage_id {
return refuse(LinkRefusal::OtherLineage);
}
if operation.work != Some(record.work) {
return refuse(LinkRefusal::OtherWork);
}
if operation.actor != record.executor {
return refuse(LinkRefusal::OtherActor);
}
if operation.timestamp < record.started_at {
return refuse(LinkRefusal::BeforeStart);
}
if ended_at.is_some_and(|ended| operation.timestamp > ended) {
return refuse(LinkRefusal::AfterEnd);
}
Ok(())
}