Skip to main content

mars_schema/
permission_set.rs

1//! One layer of an additive permission set: a plain list adds to lower layers, `replace` clears them.
2
3use std::fmt;
4use std::marker::PhantomData;
5
6use serde::de::{MapAccess, SeqAccess, Visitor};
7use serde::{Deserialize, Serialize};
8
9/// One layer of a permission set.
10///
11/// Plain lists add to lower layers. `replace` clears lower entries before adding its own.
12#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
13#[serde(untagged)]
14pub enum PermissionSet<T> {
15    Add(Vec<T>),
16    Replace { replace: Vec<T> },
17}
18
19impl<'de, T> Deserialize<'de> for PermissionSet<T>
20where
21    T: Deserialize<'de>,
22{
23    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
24    where
25        D: serde::Deserializer<'de>,
26    {
27        struct PermissionSetVisitor<T>(PhantomData<T>);
28
29        impl<'de, T> Visitor<'de> for PermissionSetVisitor<T>
30        where
31            T: Deserialize<'de>,
32        {
33            type Value = PermissionSet<T>;
34
35            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
36                formatter.write_str("a list or a table containing only `replace = [...]`")
37            }
38
39            fn visit_seq<A>(self, mut sequence: A) -> Result<Self::Value, A::Error>
40            where
41                A: SeqAccess<'de>,
42            {
43                let mut entries = Vec::with_capacity(sequence.size_hint().unwrap_or(0));
44                while let Some(entry) = sequence.next_element()? {
45                    entries.push(entry);
46                }
47                Ok(PermissionSet::Add(entries))
48            }
49
50            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
51            where
52                A: MapAccess<'de>,
53            {
54                let Some(key) = map.next_key::<String>()? else {
55                    return Err(serde::de::Error::missing_field("replace"));
56                };
57                if key != "replace" {
58                    return Err(serde::de::Error::unknown_field(&key, &["replace"]));
59                }
60                let entries = map.next_value::<Vec<T>>()?;
61                if let Some(extra) = map.next_key::<String>()? {
62                    return Err(serde::de::Error::unknown_field(&extra, &["replace"]));
63                }
64                Ok(PermissionSet::Replace { replace: entries })
65            }
66        }
67
68        deserializer.deserialize_any(PermissionSetVisitor(PhantomData))
69    }
70}
71
72impl<T> Default for PermissionSet<T> {
73    fn default() -> Self {
74        Self::Add(Vec::new())
75    }
76}
77
78impl<T> From<Vec<T>> for PermissionSet<T> {
79    fn from(entries: Vec<T>) -> Self {
80        Self::Add(entries)
81    }
82}
83
84impl<T> PermissionSet<T> {
85    pub fn entries(&self) -> &[T] {
86        match self {
87            Self::Add(entries) | Self::Replace { replace: entries } => entries,
88        }
89    }
90
91    pub fn replaces(&self) -> bool {
92        matches!(self, Self::Replace { .. })
93    }
94
95    /// An empty replacement is meaningful because it clears lower layers.
96    pub fn is_noop(&self) -> bool {
97        matches!(self, Self::Add(entries) if entries.is_empty())
98    }
99}
100
101/// Apply the permission-set rule once for every permission kind.
102pub fn merge_permission_set<T: Clone + Eq>(merged: &mut Vec<T>, layer: &PermissionSet<T>) {
103    if layer.replaces() {
104        merged.clear();
105    }
106    for entry in layer.entries() {
107        if !merged.contains(entry) {
108            merged.push(entry.clone());
109        }
110    }
111}
112
113/// Compose adjacent authored layers while preserving whether they clear still-lower layers.
114pub fn compose_permission_sets<T: Clone + Eq>(
115    lower: &PermissionSet<T>,
116    higher: &PermissionSet<T>,
117) -> PermissionSet<T> {
118    let mut entries = lower.entries().to_vec();
119    merge_permission_set(&mut entries, higher);
120    if lower.replaces() || higher.replaces() {
121        PermissionSet::Replace { replace: entries }
122    } else {
123        PermissionSet::Add(entries)
124    }
125}