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 {
76                    group: g.name.clone(),
77                });
78            }
79            if g.members.is_empty() {
80                return Err(ConfigError::EmptyGroup {
81                    group: g.name.clone(),
82                });
83            }
84        }
85
86        for g in &raw.linked {
87            if !seen_names.insert(&g.name) {
88                return Err(ConfigError::DuplicateGroupName {
89                    group: g.name.clone(),
90                });
91            }
92            if g.members.is_empty() {
93                return Err(ConfigError::EmptyGroup {
94                    group: g.name.clone(),
95                });
96            }
97        }
98
99        let mut fixed_members = BTreeMap::new();
100        for g in &raw.fixed {
101            for m in &g.members {
102                if let Some(other) = fixed_members.insert(m.as_str(), &g.name) {
103                    return Err(ConfigError::ConflictingGroupNames {
104                        group: g.name.clone(),
105                        other: (*other).clone(),
106                        member: m.clone(),
107                    });
108                }
109            }
110        }
111
112        let mut linked_members = BTreeMap::new();
113        for g in &raw.linked {
114            for m in &g.members {
115                if let Some(other) = linked_members.insert(m.as_str(), &g.name) {
116                    return Err(ConfigError::ConflictingGroupNames {
117                        group: g.name.clone(),
118                        other: (*other).clone(),
119                        member: m.clone(),
120                    });
121                }
122                if let Some(other_fixed) = fixed_members.get(m.as_str()) {
123                    return Err(ConfigError::ConflictingGroupNames {
124                        group: g.name.clone(),
125                        other: (*other_fixed).clone(),
126                        member: m.clone(),
127                    });
128                }
129            }
130        }
131
132        Ok(())
133    }
134
135    pub(crate) fn resolve(
136        raw: &RawGroupTable,
137        index: &IdentityIndex,
138    ) -> Result<GroupTable, GraphError> {
139        let mut fixed = BTreeMap::new();
140        let mut linked = BTreeMap::new();
141        let mut fixed_of = BTreeMap::new();
142        let mut linked_of = BTreeMap::new();
143
144        for rg in &raw.fixed {
145            let mut members = Vec::new();
146            for name in &rg.members {
147                if let Ok(id) = index.resolve_human(name, &[]) {
148                    members.push(GroupMember::Package(id.clone()));
149                    fixed_of.insert(id, rg.name.clone());
150                } else if let Some((owner, path)) = index.platform.get(name) {
151                    members.push(GroupMember::PlatformManifest {
152                        owner: owner.clone(),
153                        role: ManifestRole::Platform {
154                            platform: "unknown".to_string(),
155                            arch: "unknown".to_string(),
156                            abi: None,
157                        },
158                        path: path.clone(),
159                        name: name.clone(),
160                    });
161                } else {
162                    return Err(GraphError::MissingGroupMember {
163                        group: rg.name.clone(),
164                        member: name.clone(),
165                    });
166                }
167            }
168            members.sort();
169            fixed.insert(
170                rg.name.clone(),
171                GroupDef {
172                    name: rg.name.clone(),
173                    kind: GroupKind::Fixed,
174                    members,
175                },
176            );
177        }
178
179        for rg in &raw.linked {
180            let mut members = Vec::new();
181            for name in &rg.members {
182                if let Ok(id) = index.resolve_human(name, &[]) {
183                    members.push(GroupMember::Package(id.clone()));
184                    linked_of.insert(id, rg.name.clone());
185                } else if let Some((owner, path)) = index.platform.get(name) {
186                    members.push(GroupMember::PlatformManifest {
187                        owner: owner.clone(),
188                        role: ManifestRole::Platform {
189                            platform: "unknown".to_string(),
190                            arch: "unknown".to_string(),
191                            abi: None,
192                        },
193                        path: path.clone(),
194                        name: name.clone(),
195                    });
196                } else {
197                    return Err(GraphError::MissingGroupMember {
198                        group: rg.name.clone(),
199                        member: name.clone(),
200                    });
201                }
202            }
203            members.sort();
204            linked.insert(
205                rg.name.clone(),
206                GroupDef {
207                    name: rg.name.clone(),
208                    kind: GroupKind::Linked,
209                    members,
210                },
211            );
212        }
213
214        Ok(GroupTable {
215            fixed,
216            linked,
217            fixed_of,
218            linked_of,
219        })
220    }
221
222    pub fn fixed_group_of(&self, id: &PackageId) -> Option<&GroupDef> {
223        let name = self.fixed_of.get(id)?;
224        self.fixed.get(name)
225    }
226
227    pub fn linked_group_of(&self, id: &PackageId) -> Option<&GroupDef> {
228        let name = self.linked_of.get(id)?;
229        self.linked.get(name)
230    }
231
232    pub fn fixed_siblings<'a>(&'a self, id: &'a PackageId) -> impl Iterator<Item = &'a PackageId> {
233        let mut sibs = Vec::new();
234        if let Some(g) = self.fixed_group_of(id) {
235            for m in g.members(GroupMemberKind::Package) {
236                if let GroupMember::Package(ref pkg_id) = m {
237                    if pkg_id != id {
238                        sibs.push(pkg_id);
239                    }
240                }
241            }
242        }
243        sibs.into_iter()
244    }
245
246    pub fn from_groups(fixed: Vec<GroupDef>, linked: Vec<GroupDef>) -> Self {
247        let mut f_map = BTreeMap::new();
248        let mut l_map = BTreeMap::new();
249        let mut f_of = BTreeMap::new();
250        let mut l_of = BTreeMap::new();
251
252        for g in fixed {
253            for m in g.members(GroupMemberKind::Package) {
254                if let GroupMember::Package(ref id) = m {
255                    f_of.insert(id.clone(), g.name.clone());
256                }
257            }
258            f_map.insert(g.name.clone(), g);
259        }
260
261        for g in linked {
262            for m in g.members(GroupMemberKind::Package) {
263                if let GroupMember::Package(ref id) = m {
264                    l_of.insert(id.clone(), g.name.clone());
265                }
266            }
267            l_map.insert(g.name.clone(), g);
268        }
269
270        GroupTable {
271            fixed: f_map,
272            linked: l_map,
273            fixed_of: f_of,
274            linked_of: l_of,
275        }
276    }
277}