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    #[allow(dead_code)]
136    pub(crate) fn resolve(
137        raw: &RawGroupTable,
138        index: &IdentityIndex,
139    ) -> Result<GroupTable, GraphError> {
140        let mut fixed = BTreeMap::new();
141        let mut linked = BTreeMap::new();
142        let mut fixed_of = BTreeMap::new();
143        let mut linked_of = BTreeMap::new();
144
145        for rg in &raw.fixed {
146            let mut members = Vec::new();
147            for name in &rg.members {
148                if let Ok(id) = index.resolve_human(name, &[]) {
149                    members.push(GroupMember::Package(id.clone()));
150                    fixed_of.insert(id, rg.name.clone());
151                } else if let Some((owner, path)) = index.platform.get(name) {
152                    members.push(GroupMember::PlatformManifest {
153                        owner: owner.clone(),
154                        role: ManifestRole::Platform {
155                            platform: "unknown".to_string(),
156                            arch: "unknown".to_string(),
157                            abi: None,
158                        },
159                        path: path.clone(),
160                        name: name.clone(),
161                    });
162                } else {
163                    return Err(GraphError::MissingGroupMember {
164                        group: rg.name.clone(),
165                        member: name.clone(),
166                    });
167                }
168            }
169            members.sort();
170            fixed.insert(
171                rg.name.clone(),
172                GroupDef {
173                    name: rg.name.clone(),
174                    kind: GroupKind::Fixed,
175                    members,
176                },
177            );
178        }
179
180        for rg in &raw.linked {
181            let mut members = Vec::new();
182            for name in &rg.members {
183                if let Ok(id) = index.resolve_human(name, &[]) {
184                    members.push(GroupMember::Package(id.clone()));
185                    linked_of.insert(id, rg.name.clone());
186                } else if let Some((owner, path)) = index.platform.get(name) {
187                    members.push(GroupMember::PlatformManifest {
188                        owner: owner.clone(),
189                        role: ManifestRole::Platform {
190                            platform: "unknown".to_string(),
191                            arch: "unknown".to_string(),
192                            abi: None,
193                        },
194                        path: path.clone(),
195                        name: name.clone(),
196                    });
197                } else {
198                    return Err(GraphError::MissingGroupMember {
199                        group: rg.name.clone(),
200                        member: name.clone(),
201                    });
202                }
203            }
204            members.sort();
205            linked.insert(
206                rg.name.clone(),
207                GroupDef {
208                    name: rg.name.clone(),
209                    kind: GroupKind::Linked,
210                    members,
211                },
212            );
213        }
214
215        Ok(GroupTable {
216            fixed,
217            linked,
218            fixed_of,
219            linked_of,
220        })
221    }
222
223    pub fn fixed_group_of(&self, id: &PackageId) -> Option<&GroupDef> {
224        let name = self.fixed_of.get(id)?;
225        self.fixed.get(name)
226    }
227
228    pub fn linked_group_of(&self, id: &PackageId) -> Option<&GroupDef> {
229        let name = self.linked_of.get(id)?;
230        self.linked.get(name)
231    }
232
233    pub fn fixed_siblings<'a>(&'a self, id: &'a PackageId) -> impl Iterator<Item = &'a PackageId> {
234        let mut sibs = Vec::new();
235        if let Some(g) = self.fixed_group_of(id) {
236            for m in g.members(GroupMemberKind::Package) {
237                if let GroupMember::Package(ref pkg_id) = m {
238                    if pkg_id != id {
239                        sibs.push(pkg_id);
240                    }
241                }
242            }
243        }
244        sibs.into_iter()
245    }
246
247    pub fn from_groups(fixed: Vec<GroupDef>, linked: Vec<GroupDef>) -> Self {
248        let mut f_map = BTreeMap::new();
249        let mut l_map = BTreeMap::new();
250        let mut f_of = BTreeMap::new();
251        let mut l_of = BTreeMap::new();
252
253        for g in fixed {
254            for m in g.members(GroupMemberKind::Package) {
255                if let GroupMember::Package(ref id) = m {
256                    f_of.insert(id.clone(), g.name.clone());
257                }
258            }
259            f_map.insert(g.name.clone(), g);
260        }
261
262        for g in linked {
263            for m in g.members(GroupMemberKind::Package) {
264                if let GroupMember::Package(ref id) = m {
265                    l_of.insert(id.clone(), g.name.clone());
266                }
267            }
268            l_map.insert(g.name.clone(), g);
269        }
270
271        GroupTable {
272            fixed: f_map,
273            linked: l_map,
274            fixed_of: f_of,
275            linked_of: l_of,
276        }
277    }
278}