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}