1use crate::{satisfies::Satisfies, Base};
2
3use std::collections::{HashMap, HashSet};
4
5use alpm::{Alpm, Dep, DepMod, Depend};
6use raur::ArcPackage;
7
8type ConflictMap = HashMap<String, Conflict>;
9
10#[derive(Debug)]
15pub struct Actions<'a> {
16 pub(crate) alpm: &'a Alpm,
17 pub missing: Vec<Missing>,
20 pub unneeded: Vec<Unneeded>,
22 pub build: Vec<Base>,
24 pub install: Vec<RepoPackage<'a>>,
26}
27
28impl<'a> Actions<'a> {
29 pub fn iter_aur_pkgs(&self) -> impl Iterator<Item = &AurPackage> {
31 self.build
32 .iter()
33 .filter_map(|b| match b {
34 Base::Aur(pkg) => Some(&pkg.pkgs),
35 Base::Pkgbuild(_) => None,
36 })
37 .flatten()
38 }
39
40 pub fn iter_pkgbuilds(&self) -> impl Iterator<Item = (&srcinfo::Srcinfo, &Pkgbuild)> {
42 self.build
43 .iter()
44 .filter_map(|b| match b {
45 Base::Aur(_) => None,
46 Base::Pkgbuild(base) => Some((&base.srcinfo, &base.pkgs)),
47 })
48 .flat_map(|(base, pkgs)| pkgs.iter().map(move |p| (base.as_ref(), p)))
49 }
50}
51
52#[derive(Debug, Eq, Clone, PartialEq, Ord, PartialOrd, Hash)]
54pub struct Unneeded {
55 pub name: String,
57 pub version: String,
59}
60
61impl Unneeded {
62 pub fn new<S: Into<String>>(name: S, version: S) -> Self {
64 Unneeded {
65 name: name.into(),
66 version: version.into(),
67 }
68 }
69}
70
71#[derive(Debug, Eq, Clone, PartialEq, Ord, PartialOrd, Hash)]
73pub struct Package<T> {
74 pub pkg: T,
76 pub make: bool,
78 pub target: bool,
80}
81
82pub type AurPackage = Package<ArcPackage>;
84
85pub type Pkgbuild = Package<srcinfo::Package>;
87
88pub type RepoPackage<'a> = Package<&'a alpm::Package>;
90
91#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
93pub struct Conflict {
94 pub pkg: String,
96 pub conflicting: Vec<Conflicting>,
98}
99
100#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
102pub struct Conflicting {
103 pub pkg: String,
105 pub conflict: Option<String>,
107}
108
109impl Conflict {
110 pub fn new(pkg: String) -> Self {
112 Conflict {
113 pkg,
114 conflicting: Vec::with_capacity(1),
115 }
116 }
117
118 pub fn push(&mut self, pkg: String, conflict: &Dep) {
120 let conflict = if pkg != conflict.name() || conflict.depmod() != DepMod::Any {
121 Some(conflict.to_string())
122 } else {
123 None
124 };
125
126 self.conflicting.push(Conflicting { pkg, conflict });
127 }
128}
129
130#[derive(Debug, Clone, Default)]
132pub struct DepMissing {
133 pub pkg: String,
135 pub dep: Option<String>,
138}
139
140impl DepMissing {
141 pub(crate) fn new(pkg: String, dep: String) -> DepMissing {
142 DepMissing {
143 dep: (pkg != dep).then_some(dep),
144 pkg,
145 }
146 }
147}
148
149#[derive(Debug, Clone, Default)]
151pub struct Missing {
152 pub dep: String,
154 pub stack: Vec<DepMissing>,
156}
157
158impl<'a> Actions<'a> {
159 fn has_pkg<S: AsRef<str>>(&self, name: S) -> bool {
160 let name = name.as_ref();
161 let install = &self.install;
162 self.iter_aur_pkgs().any(|pkg| pkg.pkg.name == name)
163 || self.iter_pkgbuilds().any(|pkg| pkg.1.pkg.pkgname == name)
164 || install.iter().any(|pkg| pkg.pkg.name() == name)
165 }
166
167 fn check_reverse_conflict<S: AsRef<str>>(
169 &self,
170 name: S,
171 runtime: bool,
172 conflict: &Dep,
173 conflicts: &mut ConflictMap,
174 ) {
175 let name = name.as_ref();
176
177 self.install
178 .iter()
179 .filter(|pkg| !runtime || !pkg.make)
180 .map(|pkg| &pkg.pkg)
181 .filter(|pkg| pkg.name() != name)
182 .filter(|pkg| pkg.satisfies_dep(conflict, false))
183 .for_each(|pkg| {
184 conflicts
185 .entry(pkg.name().to_string())
186 .or_insert_with(|| Conflict::new(pkg.name().to_string()))
187 .push(name.to_string(), conflict);
188 });
189
190 self.iter_aur_pkgs()
191 .filter(|pkg| !runtime || !pkg.make)
192 .map(|pkg| &pkg.pkg)
193 .filter(|pkg| pkg.name != name)
194 .filter(|pkg| pkg.satisfies_dep(conflict, false))
195 .for_each(|pkg| {
196 conflicts
197 .entry(pkg.name.to_string())
198 .or_insert_with(|| Conflict::new(pkg.name.to_string()))
199 .push(name.to_string(), conflict);
200 });
201 self.iter_pkgbuilds()
202 .filter(|(_, pkg)| !runtime || !pkg.make)
203 .filter(|(_, pkg)| pkg.pkg.pkgname != name)
204 .filter(|(base, pkg)| (*base, &pkg.pkg).satisfies_dep(conflict, false))
205 .map(|pkg| &pkg.1.pkg)
206 .for_each(|pkg| {
207 conflicts
208 .entry(pkg.pkgname.clone())
209 .or_insert_with(|| Conflict::new(pkg.pkgname.to_string()))
210 .push(name.to_string(), conflict);
211 });
212 }
213
214 fn check_forward_conflict<S: AsRef<str>>(
216 &self,
217 name: S,
218 conflict: &Dep,
219 conflicts: &mut ConflictMap,
220 ) {
221 let name = name.as_ref();
222 self.alpm
223 .localdb()
224 .pkgs()
225 .iter()
226 .filter(|pkg| !self.has_pkg(pkg.name()))
227 .filter(|pkg| pkg.name() != name)
228 .filter(|pkg| pkg.satisfies_dep(conflict, false))
229 .for_each(|pkg| {
230 conflicts
231 .entry(name.to_string())
232 .or_insert_with(|| Conflict::new(name.to_string()))
233 .push(pkg.name().to_string(), conflict);
234 });
235 }
236
237 fn check_forward_conflicts(&self, runtime: bool, conflicts: &mut ConflictMap) {
238 for pkg in self.install.iter() {
239 if runtime && pkg.make {
240 continue;
241 }
242
243 for conflict in pkg.pkg.conflicts() {
244 self.check_forward_conflict(pkg.pkg.name(), &conflict, conflicts);
245 }
246 }
247
248 for pkg in self.iter_aur_pkgs() {
249 if runtime && pkg.make {
250 continue;
251 }
252
253 for conflict in &pkg.pkg.conflicts {
254 self.check_forward_conflict(
255 &pkg.pkg.name,
256 &Depend::new(conflict.to_string()),
257 conflicts,
258 );
259 }
260 }
261 for (_, pkg) in self.iter_pkgbuilds() {
262 if runtime && pkg.make {
263 continue;
264 }
265
266 for conflict in pkg
267 .pkg
268 .conflicts
269 .iter()
270 .filter(|c| {
271 c.arch.is_none() || c.arch.as_deref() == self.alpm.architectures().first()
272 })
273 .flat_map(|c| &c.vec)
274 {
275 self.check_forward_conflict(
276 &pkg.pkg.pkgname,
277 &Depend::new(conflict.clone()),
278 conflicts,
279 );
280 }
281 }
282 }
283
284 fn check_inner_conflicts(&self, runtime: bool, conflicts: &mut ConflictMap) {
285 for pkg in self.install.iter() {
286 if runtime && pkg.make {
287 continue;
288 }
289
290 for conflict in pkg.pkg.conflicts() {
291 self.check_reverse_conflict(pkg.pkg.name(), runtime, &conflict, conflicts)
292 }
293 }
294
295 for pkg in self.iter_aur_pkgs() {
296 if runtime && pkg.make {
297 continue;
298 }
299
300 for conflict in pkg.pkg.conflicts.iter() {
301 self.check_reverse_conflict(
302 &pkg.pkg.name,
303 runtime,
304 &Depend::new(conflict.to_string()),
305 conflicts,
306 )
307 }
308 }
309
310 for (_, pkg) in self.iter_pkgbuilds() {
311 if runtime && pkg.make {
312 continue;
313 }
314
315 for conflict in pkg
316 .pkg
317 .conflicts
318 .iter()
319 .filter(|c| {
320 c.arch.is_none() || c.arch.as_deref() == self.alpm.architectures().first()
321 })
322 .flat_map(|c| &c.vec)
323 {
324 self.check_reverse_conflict(
325 &pkg.pkg.pkgname,
326 runtime,
327 &Depend::new(conflict.to_string()),
328 conflicts,
329 )
330 }
331 }
332 }
333
334 fn check_reverse_conflicts(&self, runtime: bool, conflicts: &mut ConflictMap) {
335 self.alpm
336 .localdb()
337 .pkgs()
338 .iter()
339 .filter(|pkg| !self.has_pkg(pkg.name()))
340 .for_each(|pkg| {
341 pkg.conflicts().iter().for_each(|conflict| {
342 self.check_reverse_conflict(pkg.name(), runtime, &conflict, conflicts)
343 })
344 });
345 }
346
347 pub fn calculate_conflicts(&self, makedeps: bool) -> Vec<Conflict> {
358 let mut conflicts = ConflictMap::new();
359
360 self.check_reverse_conflicts(!makedeps, &mut conflicts);
361 self.check_forward_conflicts(!makedeps, &mut conflicts);
362
363 let mut conflicts = conflicts.into_values().collect::<Vec<Conflict>>();
364
365 conflicts.sort();
366 conflicts
367 }
368
369 pub fn calculate_inner_conflicts(&self, makedeps: bool) -> Vec<Conflict> {
380 let mut inner_conflicts = ConflictMap::new();
381
382 self.check_inner_conflicts(!makedeps, &mut inner_conflicts);
383
384 let mut inner_conflicts = inner_conflicts.into_values().collect::<Vec<Conflict>>();
385
386 inner_conflicts.sort();
387 inner_conflicts
388 }
389
390 pub fn duplicate_targets(&self) -> Vec<String> {
393 let mut names = HashSet::new();
394
395 let build = self.iter_aur_pkgs().map(|pkg| pkg.pkg.name.as_str());
396 let pkgbuilds = self.iter_pkgbuilds().map(|pkg| pkg.1.pkg.pkgname.as_str());
397
398 let duplicates = self
399 .install
400 .iter()
401 .map(|pkg| pkg.pkg.name())
402 .chain(build)
403 .chain(pkgbuilds)
404 .filter(|&name| !names.insert(name))
405 .map(Into::into)
406 .collect::<Vec<_>>();
407
408 duplicates
409 }
410}