Skip to main content

sofie_model/
cross_owner.rs

1use std::path::{Path, PathBuf};
2
3use crate::Code;
4use crate::effects::Effects;
5
6/// A call of an aggregate's crate-private `across`, which reads past the owner filter.
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct CrossOwnerCheck {
9    pub aggregate: String,
10    /// `actions::book_slot`, or `Booking::reschedule` for a method.
11    pub caller: String,
12    pub file: PathBuf,
13    pub line: usize,
14}
15
16impl Code {
17    #[must_use]
18    pub fn cross_owner_checks(&self) -> Vec<CrossOwnerCheck> {
19        let functions = self.functions.iter().map(|function| {
20            let caller = function.path.join("::");
21            self.checks_in(&caller, &function.effects, &function.file)
22        });
23        let methods = self.methods.iter().map(|method| {
24            let caller = format!("{}::{}", method.owner, method.name);
25            self.checks_in(&caller, &method.effects, &method.file)
26        });
27        functions.chain(methods).flatten().collect()
28    }
29
30    fn checks_in(&self, caller: &str, effects: &Effects, file: &Path) -> Vec<CrossOwnerCheck> {
31        effects
32            .calls
33            .iter()
34            .filter_map(|call| {
35                let [.., aggregate, method] = call.path.as_slice() else {
36                    return None;
37                };
38                let known = self.aggregates.iter().any(|known| known.name == *aggregate);
39                (method == "across" && known).then(|| CrossOwnerCheck {
40                    aggregate: aggregate.clone(),
41                    caller: caller.to_owned(),
42                    file: file.to_path_buf(),
43                    line: call.line,
44                })
45            })
46            .collect()
47    }
48}