1use sofie_core::Result;
2use sofie_db::{Call, Outcome, Plan, Reply, Stamp};
3
4use crate::{Feature, Flags, Subject};
5
6pub fn gated<H>(flags: &Flags, feature: Feature, handler: H) -> Gated<H> {
8 Gated {
9 flags: flags.clone(),
10 feature,
11 handler,
12 }
13}
14
15#[derive(Clone)]
16pub struct Gated<H> {
17 flags: Flags,
18 feature: Feature,
19 handler: H,
20}
21
22impl<C: Send, Args, H: Call<C, Args>> Call<C, Args> for Gated<H> {
23 type Outcome = Skippable<H::Outcome>;
24
25 async fn call(&self, context: &mut C) -> Result<Self::Outcome> {
26 let features = self.flags.evaluate(Subject::default());
27 if !features.enabled(self.feature) {
28 let name = self.feature.name();
29 tracing::info!(feature_flag.key = name, "skipped: its feature is off");
30 return Ok(Skippable(None));
31 }
32 sofie_telemetry::attach_feature_flags(&tracing::Span::current(), move || {
33 features.evaluated()
34 });
35 self.handler
36 .call(context)
37 .await
38 .map(|outcome| Skippable(Some(outcome)))
39 }
40}
41
42pub struct Skippable<O>(Option<O>);
43
44impl<O: Outcome> Outcome for Skippable<O> {
45 fn plan(&self, plan: &mut Plan<'_>) -> Result<()> {
46 self.0.as_ref().map_or(Ok(()), |outcome| outcome.plan(plan))
47 }
48
49 fn committed(&mut self, stamp: Stamp) {
50 if let Some(outcome) = &mut self.0 {
51 outcome.committed(stamp);
52 }
53 }
54
55 fn reply(self) -> Result<Reply> {
56 self.0.map_or_else(|| ().reply(), Outcome::reply)
57 }
58}