Skip to main content

greentic_bundle/access/
eval.rs

1use super::{GmapPath, GmapRule, Policy};
2
3#[derive(Debug, Clone, Eq, PartialEq)]
4pub struct MatchDecision {
5    pub policy: Policy,
6    pub rank: u8,
7}
8
9pub fn eval_policy(rules: &[GmapRule], target: &GmapPath) -> Option<MatchDecision> {
10    let mut best: Option<MatchDecision> = None;
11    let mut best_index = 0usize;
12    for (idx, rule) in rules.iter().enumerate() {
13        if !matches_target(&rule.path, target) {
14            continue;
15        }
16        let rank = specificity_rank(&rule.path);
17        let candidate = MatchDecision {
18            policy: rule.policy.clone(),
19            rank,
20        };
21        match best {
22            None => {
23                best = Some(candidate);
24                best_index = idx;
25            }
26            Some(ref current) => {
27                if rank > current.rank || (rank == current.rank && idx > best_index) {
28                    best = Some(candidate);
29                    best_index = idx;
30                }
31            }
32        }
33    }
34    best
35}
36
37pub fn eval_with_overlay(
38    tenant_rules: &[GmapRule],
39    team_rules: &[GmapRule],
40    target: &GmapPath,
41) -> Option<MatchDecision> {
42    eval_policy(team_rules, target).or_else(|| eval_policy(tenant_rules, target))
43}
44
45fn matches_target(rule: &GmapPath, target: &GmapPath) -> bool {
46    match (&rule.pack, &rule.flow, &rule.node) {
47        (None, None, None) => true,
48        (Some(pack), None, None) => target.pack.as_deref() == Some(pack.as_str()),
49        (Some(pack), Some(flow), None) => {
50            if flow == "_" {
51                target.pack.as_deref() == Some(pack.as_str())
52            } else {
53                target.pack.as_deref() == Some(pack.as_str())
54                    && target.flow.as_deref() == Some(flow.as_str())
55            }
56        }
57        (Some(pack), Some(flow), Some(node)) => {
58            target.pack.as_deref() == Some(pack.as_str())
59                && target.flow.as_deref() == Some(flow.as_str())
60                && target.node.as_deref() == Some(node.as_str())
61        }
62        _ => false,
63    }
64}
65
66fn specificity_rank(path: &GmapPath) -> u8 {
67    match (&path.pack, &path.flow, &path.node) {
68        (None, None, None) => 0,
69        (Some(_), None, None) => 3,
70        (Some(_), Some(flow), None) if flow == "_" => 2,
71        (Some(_), Some(_), None) => 4,
72        (Some(_), Some(_), Some(_)) => 5,
73        _ => 0,
74    }
75}