Skip to main content

ephemeris_forge_github/plan/
rulesets.rs

1use ephemeris_forge::{Caller, Collection, FieldDiff, Report};
2
3use crate::api::model;
4use crate::operation::rulesets::Operation;
5use crate::{Ruleset, Rulesets};
6
7pub(super) fn plan(
8    config: Option<&Rulesets>,
9    live: &[model::Ruleset],
10    caller: Option<&Caller>,
11    report: &mut Report,
12) -> Vec<Operation> {
13    let Some(config) = config else {
14        return Vec::new();
15    };
16    for ruleset in live {
17        report
18            .limitations
19            .extend(ruleset.limitations.iter().cloned());
20    }
21    Collection {
22        noun: "ruleset",
23        mode: config.mode,
24    }
25    .plan(
26        &config.rulesets,
27        live,
28        report,
29        |ruleset, name| ruleset.name == name,
30        |name, desired, actual, _| {
31            let desired = match caller {
32                Some(Caller::App {
33                    id,
34                    slug: _,
35                    admin: _,
36                }) => i64::try_from(*id)
37                    .map_or_else(|_| desired.clone(), |id| desired.clone().with_own_app(id)),
38                _ => desired.clone(),
39            };
40            let diff = compare(&desired, actual);
41            let (name, ruleset) = (name.to_owned(), Box::new(desired));
42            let operation = match actual {
43                _ if !diff.has_changes() => None,
44                Some(actual) => Some(Operation::Update {
45                    id: actual.id,
46                    name,
47                    ruleset,
48                    unmanaged_rules: actual.unmanaged_rules.clone(),
49                }),
50                None => Some(Operation::Create { name, ruleset }),
51            };
52            (diff, operation.into_iter().collect())
53        },
54        |ruleset, _| {
55            Some((
56                ruleset.name.clone(),
57                Operation::Delete {
58                    id: ruleset.id,
59                    name: ruleset.name.clone(),
60                },
61            ))
62        },
63    )
64}
65
66fn compare(desired: &Ruleset, live: Option<&model::Ruleset>) -> FieldDiff {
67    let desired = desired.clone().normalized();
68    let live_definition = live.map(|live| live.definition.clone().normalized());
69    let live_definition = live_definition.as_ref();
70    let mut diff = FieldDiff::default();
71    diff.compare(
72        "target",
73        live_definition.map(|actual| &actual.target),
74        &desired.target,
75    );
76    diff.compare(
77        "enforcement",
78        live_definition.map(|actual| &actual.enforcement),
79        &desired.enforcement,
80    );
81    diff.compare(
82        "include",
83        live_definition.map(|actual| &actual.include),
84        &desired.include,
85    );
86    diff.compare(
87        "exclude",
88        live_definition.map(|actual| &actual.exclude),
89        &desired.exclude,
90    );
91    diff.compare(
92        "bypass_actors",
93        live_definition.map(|actual| &actual.bypass_actors),
94        &desired.bypass_actors,
95    );
96    desired
97        .rules
98        .compare(live_definition.map(|actual| &actual.rules), &mut diff);
99    diff
100}