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}