Skip to main content

callisto_graph/config/
groups.rs

1use std::collections::{BTreeMap, BTreeSet};
2use std::path::PathBuf;
3
4use callisto_model::{GroupKind, GroupName, ManifestRole, PackageId};
5use serde::Deserialize;
6
7use crate::error::{ConfigError, GraphError};
8use crate::identity::IdentityIndex;
9
10#[derive(Clone, Debug, Default, PartialEq, Eq)]
11pub struct GroupTable {
12    pub fixed: BTreeMap<GroupName, GroupDef>,
13    pub linked: BTreeMap<GroupName, GroupDef>,
14    pub fixed_of: BTreeMap<PackageId, GroupName>,
15    pub linked_of: BTreeMap<PackageId, GroupName>,
16}
17
18#[derive(Clone, Debug, PartialEq, Eq)]
19pub struct GroupDef {
20    pub name: GroupName,
21    pub kind: GroupKind,
22    pub members: Vec<GroupMember>,
23}
24
25#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
26pub enum GroupMember {
27    Package(PackageId),
28    PlatformManifest {
29        owner: PackageId,
30        role: ManifestRole,
31        path: PathBuf,
32        name: String,
33    },
34}
35
36#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
37pub enum GroupMemberKind {
38    Package,
39    PlatformManifest,
40}
41
42impl GroupMember {
43    pub fn kind(&self) -> GroupMemberKind {
44        match self {
45            GroupMember::Package(_) => GroupMemberKind::Package,
46            GroupMember::PlatformManifest { .. } => GroupMemberKind::PlatformManifest,
47        }
48    }
49}
50
51impl GroupDef {
52    pub fn members(&self, kind: GroupMemberKind) -> impl Iterator<Item = &GroupMember> {
53        self.members.iter().filter(move |m| m.kind() == kind)
54    }
55}
56
57#[derive(Clone, Debug, Default, Deserialize)]
58pub struct RawGroupTable {
59    pub fixed: Vec<RawGroup>,
60    pub linked: Vec<RawGroup>,
61}
62
63#[derive(Clone, Debug, Deserialize)]
64pub struct RawGroup {
65    pub name: GroupName,
66    pub members: Vec<String>,
67}
68
69impl GroupTable {
70    pub(crate) fn validate_syntactic(raw: &RawGroupTable) -> Result<(), ConfigError> {
71        let mut seen_names = BTreeSet::new();
72
73        for g in &raw.fixed {
74            if !seen_names.insert(&g.name) {
75                return Err(ConfigError::DuplicateGroupName { group: g.name.clone() });
76            }
77            if g.members.is_empty() {
78                return Err(ConfigError::EmptyGroup { group: g.name.clone() });
79            }
80        }
81
82        for g in &raw.linked {
83            if !seen_names.insert(&g.name) {
84                return Err(ConfigError::DuplicateGroupName { group: g.name.clone() });
85            }
86            if g.members.is_empty() {
87                return Err(ConfigError::EmptyGroup { group: g.name.clone() });
88            }
89        }
90
91        let mut fixed_members = BTreeMap::new();
92        for g in &raw.fixed {
93            for m in &g.members {
94                if let Some(other) = fixed_members.insert(m.as_str(), &g.name) {
95                    return Err(ConfigError::ConflictingGroupNames {
96                        group: g.name.clone(),
97                        other: (*other).clone(),
98                        member: m.clone(),
99                    });
100                }
101            }
102        }
103
104        let mut linked_members = BTreeMap::new();
105        for g in &raw.linked {
106            for m in &g.members {
107                if let Some(other) = linked_members.insert(m.as_str(), &g.name) {
108                    return Err(ConfigError::ConflictingGroupNames {
109                        group: g.name.clone(),
110                        other: (*other).clone(),
111                        member: m.clone(),
112                    });
113                }
114                if let Some(other_fixed) = fixed_members.get(m.as_str()) {
115                    return Err(ConfigError::ConflictingGroupNames {
116                        group: g.name.clone(),
117                        other: (*other_fixed).clone(),
118                        member: m.clone(),
119                    });
120                }
121            }
122        }
123
124        Ok(())
125    }
126
127    pub(crate) fn resolve(raw: &RawGroupTable, index: &IdentityIndex) -> Result<GroupTable, GraphError> {
128        let mut fixed = BTreeMap::new();
129        let mut linked = BTreeMap::new();
130        let mut fixed_of = BTreeMap::new();
131        let mut linked_of = BTreeMap::new();
132        let mut claimed_by: BTreeMap<PackageId, GroupName> = BTreeMap::new();
133
134        for rg in &raw.fixed {
135            let mut members = Vec::new();
136            for name in &rg.members {
137                if let Ok(id) = index.resolve_human(name, &[]) {
138                    if let Some(other) = claimed_by.get(&id) {
139                        if other != &rg.name {
140                            return Err(GraphError::ConflictingGroupMembership {
141                                package: id.clone(),
142                                groups: vec![other.clone(), rg.name.clone()],
143                            });
144                        }
145                    } else {
146                        claimed_by.insert(id.clone(), rg.name.clone());
147                    }
148                    members.push(GroupMember::Package(id.clone()));
149                    fixed_of.insert(id, rg.name.clone());
150                } else if let Some((owner, path, role)) = index.platform.get(name) {
151                    members.push(GroupMember::PlatformManifest {
152                        owner: owner.clone(),
153                        role: role.clone(),
154                        path: path.clone(),
155                        name: name.clone(),
156                    });
157                } else {
158                    return Err(GraphError::MissingGroupMember {
159                        group: rg.name.clone(),
160                        member: name.clone(),
161                    });
162                }
163            }
164            members.sort();
165            fixed.insert(
166                rg.name.clone(),
167                GroupDef {
168                    name: rg.name.clone(),
169                    kind: GroupKind::Fixed,
170                    members,
171                },
172            );
173        }
174
175        for rg in &raw.linked {
176            let mut members = Vec::new();
177            for name in &rg.members {
178                if let Ok(id) = index.resolve_human(name, &[]) {
179                    if let Some(other) = claimed_by.get(&id) {
180                        if other != &rg.name {
181                            return Err(GraphError::ConflictingGroupMembership {
182                                package: id.clone(),
183                                groups: vec![other.clone(), rg.name.clone()],
184                            });
185                        }
186                    } else {
187                        claimed_by.insert(id.clone(), rg.name.clone());
188                    }
189                    members.push(GroupMember::Package(id.clone()));
190                    linked_of.insert(id, rg.name.clone());
191                } else if let Some((owner, path, role)) = index.platform.get(name) {
192                    members.push(GroupMember::PlatformManifest {
193                        owner: owner.clone(),
194                        role: role.clone(),
195                        path: path.clone(),
196                        name: name.clone(),
197                    });
198                } else {
199                    return Err(GraphError::MissingGroupMember {
200                        group: rg.name.clone(),
201                        member: name.clone(),
202                    });
203                }
204            }
205            members.sort();
206            linked.insert(
207                rg.name.clone(),
208                GroupDef {
209                    name: rg.name.clone(),
210                    kind: GroupKind::Linked,
211                    members,
212                },
213            );
214        }
215
216        Ok(GroupTable {
217            fixed,
218            linked,
219            fixed_of,
220            linked_of,
221        })
222    }
223
224    pub fn fixed_group_of(&self, id: &PackageId) -> Option<&GroupDef> {
225        let name = self.fixed_of.get(id)?;
226        self.fixed.get(name)
227    }
228
229    pub fn linked_group_of(&self, id: &PackageId) -> Option<&GroupDef> {
230        let name = self.linked_of.get(id)?;
231        self.linked.get(name)
232    }
233
234    pub fn fixed_siblings<'a>(&'a self, id: &'a PackageId) -> impl Iterator<Item = &'a PackageId> {
235        let mut sibs = Vec::new();
236        if let Some(g) = self.fixed_group_of(id) {
237            for m in g.members(GroupMemberKind::Package) {
238                if let GroupMember::Package(ref pkg_id) = m {
239                    if pkg_id != id {
240                        sibs.push(pkg_id);
241                    }
242                }
243            }
244        }
245        sibs.into_iter()
246    }
247
248    pub fn from_groups(fixed: Vec<GroupDef>, linked: Vec<GroupDef>) -> Self {
249        let mut f_map = BTreeMap::new();
250        let mut l_map = BTreeMap::new();
251        let mut f_of = BTreeMap::new();
252        let mut l_of = BTreeMap::new();
253
254        for g in fixed {
255            for m in g.members(GroupMemberKind::Package) {
256                if let GroupMember::Package(ref id) = m {
257                    f_of.insert(id.clone(), g.name.clone());
258                }
259            }
260            f_map.insert(g.name.clone(), g);
261        }
262
263        for g in linked {
264            for m in g.members(GroupMemberKind::Package) {
265                if let GroupMember::Package(ref id) = m {
266                    l_of.insert(id.clone(), g.name.clone());
267                }
268            }
269            l_map.insert(g.name.clone(), g);
270        }
271
272        GroupTable {
273            fixed: f_map,
274            linked: l_map,
275            fixed_of: f_of,
276            linked_of: l_of,
277        }
278    }
279}