1use super::{LinkRefusal, RunError};
4use crate::{Command, Operation};
5use chrono::{DateTime, Utc};
6use dpm_model::{ActorId, LineageId, OperationId, RunRecord, WorkItemId, WorkspaceId};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct OperationFacts {
11 pub id: OperationId,
13 pub actor: ActorId,
15 pub timestamp: DateTime<Utc>,
17 pub work: Option<WorkItemId>,
19 pub workspace: WorkspaceId,
21 pub lineage: LineageId,
23}
24
25impl OperationFacts {
26 #[must_use]
28 pub fn of(operation: &Operation, workspace: WorkspaceId, lineage: LineageId) -> Self {
29 Self {
30 id: operation.id,
31 actor: operation.actor.clone(),
32 timestamp: operation.timestamp,
33 work: command_work(&operation.command),
34 workspace,
35 lineage,
36 }
37 }
38}
39
40fn command_work(command: &Command) -> Option<WorkItemId> {
44 match command {
45 Command::RatifyContract { work }
46 | Command::Reject { work, .. }
47 | Command::Claim { work }
48 | Command::Release { work, .. }
49 | Command::Handoff { work, .. }
50 | Command::Start { work, .. }
51 | Command::Block { work, .. }
52 | Command::Unblock { work }
53 | Command::ReportProgress { work, .. }
54 | Command::Submit { work, .. }
55 | Command::Verify { work, .. }
56 | Command::AttachArtifact { work, .. }
57 | Command::UnlinkExternal { work, .. }
58 | Command::RevalidateBasis { work, .. } => Some(*work),
59 Command::LinkExternal(request) => Some(request.work),
60 Command::ApplyChange { .. }
61 | Command::RevertChange { .. }
62 | Command::WaiveDependency { .. }
63 | Command::RestoreDependency { .. }
64 | Command::Decide { .. } => None,
65 }
66}
67
68pub fn check_link(
75 record: &RunRecord,
76 ended_at: Option<DateTime<Utc>>,
77 operation: &OperationFacts,
78) -> Result<(), RunError> {
79 let refuse = |reason| {
80 Err(RunError::LinkRefused {
81 run: record.id,
82 operation: operation.id,
83 reason,
84 })
85 };
86 if operation.workspace != record.contract.workspace_id {
87 return refuse(LinkRefusal::OtherWorkspace);
88 }
89 if operation.lineage != record.contract.lineage_id {
90 return refuse(LinkRefusal::OtherLineage);
91 }
92 if operation.work != Some(record.work) {
93 return refuse(LinkRefusal::OtherWork);
94 }
95 if operation.actor != record.executor {
96 return refuse(LinkRefusal::OtherActor);
97 }
98 if operation.timestamp < record.started_at {
99 return refuse(LinkRefusal::BeforeStart);
100 }
101 if ended_at.is_some_and(|ended| operation.timestamp > ended) {
102 return refuse(LinkRefusal::AfterEnd);
103 }
104 Ok(())
105}