1use sofie_core::{Cast, Error, Instant, Result, SqlValue};
2
3use crate::behavior::{stored, stored_time};
4use crate::requested::{STAMP_REQUESTED, stamp_requested, stored_mut};
5use crate::{Aggregate, Behavior, Column, Field, Plan, Uses, Write};
6
7pub struct PublishLock;
8
9impl Behavior for PublishLock {
10 fn columns(&self) -> &'static [Field] {
11 const { &[Field::new("published_at", Instant::COLUMNS, true)] }
12 }
13
14 fn written(&self, write: &Write<'_>, values: &mut [SqlValue]) {
15 stamp_requested(write, &mut values[0]);
16 }
17
18 fn planned(&self, write: &Write<'_>, _plan: &mut Plan<'_>) -> Result<()> {
19 let published = write
20 .loaded("published_at")
21 .is_some_and(|value| *value != SqlValue::Null);
22 if published && !write.changed_fields().is_empty() {
23 return Err(Error::Rule(format!(
24 "a published {} cannot be changed; supersede it with a new one",
25 write.table.name
26 )));
27 }
28 Ok(())
29 }
30}
31
32pub trait Publishes: Aggregate {
33 const PUBLISHED_AT: Column<Self, Option<Instant>> = Column::new("published_at");
34
35 fn publish(&mut self) {
36 if !self.is_published() {
37 *stored_mut::<Self>(self, "published_at") = STAMP_REQUESTED;
38 }
39 }
40
41 fn is_published(&self) -> bool {
42 stored::<Self>(self, "published_at").is_some_and(|value| *value != SqlValue::Null)
43 }
44
45 fn published_at(&self) -> Option<Instant> {
46 stored_time::<Self>(self, "published_at")
47 }
48}
49
50impl<A: Uses<PublishLock>> Publishes for A {}
51
52pub struct Broadcasts;
54
55impl Behavior for Broadcasts {
56 fn columns(&self) -> &'static [Field] {
57 &[]
58 }
59
60 fn written(&self, _write: &Write<'_>, _values: &mut [SqlValue]) {}
61
62 fn broadcasts(&self) -> bool {
63 true
64 }
65}