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}