1use sofie_core::{Error, Result, SqlValue, Timestamp};
2
3use crate::{BoxFuture, Db, Step, Wakeups, query_values};
4
5pub 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, ¶ms).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}