Skip to main content

sofie_db/
shield.rs

1use sofie_core::{Error, Result, SqlValue, Timestamp};
2
3use crate::{BoxFuture, Db, Step, Wakeups, query_values};
4
5/// `notify` is sent on every change.
6pub struct ShieldSql {
7    pub upsert: &'static str,
8    pub delete: &'static str,
9    pub active: &'static str,
10    pub notify: &'static str,
11}
12
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct SharedBlock {
15    pub key: String,
16    pub rule: String,
17    pub grade: String,
18    pub until: Timestamp,
19}
20
21pub struct SharedBlocks<'d> {
22    db: &'d Db,
23}
24
25impl<'d> SharedBlocks<'d> {
26    #[must_use]
27    pub fn of(db: &'d Db) -> Self {
28        Self { db }
29    }
30
31    fn sql(&self) -> Result<&'static ShieldSql> {
32        self.db
33            .driver()
34            .dialect()
35            .shield()
36            .ok_or_else(|| Error::internal_message("this database keeps no shared shield blocks"))
37    }
38
39    pub async fn share(&self, block: &SharedBlock) -> Result<()> {
40        let sql = self.sql()?;
41        let params = vec![
42            SqlValue::Text(block.key.clone()),
43            SqlValue::Text(block.rule.clone()),
44            SqlValue::Text(block.grade.clone()),
45            SqlValue::Timestamp(block.until),
46        ];
47        self.db
48            .transaction(vec![Step::statement(sql.upsert, params, None), notify(sql)])
49            .await
50    }
51
52    pub async fn lift(&self, key: &str) -> Result<()> {
53        let sql = self.sql()?;
54        let params = vec![SqlValue::Text(key.to_owned())];
55        self.db
56            .transaction(vec![Step::statement(sql.delete, params, None), notify(sql)])
57            .await
58    }
59
60    pub async fn active(&self, now: Timestamp) -> Result<Vec<SharedBlock>> {
61        let params = [SqlValue::Timestamp(now)];
62        let rows = query_values(self.db.driver(), self.sql()?.active, &params).await?;
63        rows.into_iter().map(shared_block).collect()
64    }
65
66    #[must_use]
67    pub fn wakeups(&self) -> Option<BoxFuture<'d, Result<Box<dyn Wakeups>>>> {
68        self.db.driver().shield_wakeups()
69    }
70}
71
72fn notify(sql: &ShieldSql) -> Step {
73    Step::statement(sql.notify, Vec::new(), None)
74}
75
76fn shared_block(row: Vec<SqlValue>) -> Result<SharedBlock> {
77    match <[SqlValue; 4]>::try_from(row) {
78        Ok(
79            [
80                SqlValue::Text(key),
81                SqlValue::Text(rule),
82                SqlValue::Text(grade),
83                SqlValue::Timestamp(until),
84            ],
85        ) => Ok(SharedBlock {
86            key,
87            rule,
88            grade,
89            until,
90        }),
91        _ => Err(Error::internal_message(
92            "a shared shield block is not `key, rule, grade, until`",
93        )),
94    }
95}