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 let conflicting = Conflicting { pkg, conflict };
127 if !self.conflicting.contains(&conflicting) {
128 self.conflicting.push(conflicting);
129 }
130 }
131}
132
133#[derive(Debug, Clone, Default)]
135pub struct DepMissing {
136 pub pkg: String,
138 pub dep: Option<String>,
141}
142
143impl DepMissing {
144 pub(crate) fn new(pkg: String, dep: String) -> DepMissing {
145 DepMissing {
146 dep: (pkg != dep).then_some(dep),
147 pkg,
148 }
149 }
150}
151
152#[derive(Debug, Clone, Default)]
154pub struct Missing {
155 pub dep: String,
157 pub stack: Vec<DepMissing>,
159}
160
161impl<'a> Actions<'a> {
162 fn has_pkg<S: AsRef<str>>(&self, name: S) -> bool {
163 let name = name.as_ref();
164 let install = &self.install;
165 self.iter_aur_pkgs().any(|pkg| pkg.pkg.name == name)
166 || self.iter_pkgbuilds().any(|pkg| pkg.1.pkg.pkgname == name)
167 || install.iter().any(|pkg| pkg.pkg.name() == name)
168 }
169
170 fn arch(&self) -> &str {
171 self.alpm.architectures().first().unwrap_or_default()
177 }
178
179 fn check_reverse_conflict<S: AsRef<str>>(
181 &self,
182 name: S,
183 runtime: bool,
184 conflict: &Dep,
185 conflicts: &mut ConflictMap,
186 ) {
187 let name = name.as_ref();
188
189 self.install
190 .iter()
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
202 self.iter_aur_pkgs()
203 .filter(|pkg| !runtime || !pkg.make)
204 .map(|pkg| &pkg.pkg)
205 .filter(|pkg| pkg.name != name)
206 .filter(|pkg| pkg.satisfies_dep(conflict, false))
207 .for_each(|pkg| {
208 conflicts
209 .entry(pkg.name.to_string())
210 .or_insert_with(|| Conflict::new(pkg.name.to_string()))
211 .push(name.to_string(), conflict);
212 });
213 self.iter_pkgbuilds()
214 .filter(|(_, pkg)| !runtime || !pkg.make)
215 .filter(|(_, pkg)| pkg.pkg.pkgname != name)
216 .filter(|(base, pkg)| (*base, &pkg.pkg).satisfies_dep(conflict, false))
217 .map(|pkg| &pkg.1.pkg)
218 .for_each(|pkg| {
219 conflicts
220 .entry(pkg.pkgname.clone())
221 .or_insert_with(|| Conflict::new(pkg.pkgname.to_string()))
222 .push(name.to_string(), conflict);
223 });
224 }
225
226 fn check_forward_conflict<S: AsRef<str>>(
228 &self,
229 name: S,
230 conflict: &Dep,
231 conflicts: &mut ConflictMap,
232 ) {
233 let name = name.as_ref();
234 self.alpm
235 .localdb()
236 .pkgs()
237 .iter()
238 .filter(|pkg| !self.has_pkg(pkg.name()))
239 .filter(|pkg| pkg.name() != name)
240 .filter(|pkg| pkg.satisfies_dep(conflict, false))
241 .for_each(|pkg| {
242 conflicts
243 .entry(name.to_string())
244 .or_insert_with(|| Conflict::new(name.to_string()))
245 .push(pkg.name().to_string(), conflict);
246 });
247 }
248
249 fn check_forward_conflicts(&self, runtime: bool, conflicts: &mut ConflictMap) {
250 for pkg in self.install.iter() {
251 if runtime && pkg.make {
252 continue;
253 }
254
255 for conflict in pkg.pkg.conflicts() {
256 self.check_forward_conflict(pkg.pkg.name(), conflict, conflicts);
257 }
258 }
259
260 for pkg in self.iter_aur_pkgs() {
261 if runtime && pkg.make {
262 continue;
263 }
264
265 for conflict in &pkg.pkg.conflicts {
266 self.check_forward_conflict(
267 &pkg.pkg.name,
268 &Depend::new(conflict.to_string()),
269 conflicts,
270 );
271 }
272 }
273 for (_, pkg) in self.iter_pkgbuilds() {
274 if runtime && pkg.make {
275 continue;
276 }
277
278 for conflict in pkg.pkg.conflicts.arch(self.arch()) {
279 self.check_forward_conflict(
280 &pkg.pkg.pkgname,
281 &Depend::new(conflict),
282 conflicts,
283 );
284 }
285 }
286 }
287
288 fn check_inner_conflicts(&self, runtime: bool, conflicts: &mut ConflictMap) {
289 for pkg in self.install.iter() {
290 if runtime && pkg.make {
291 continue;
292 }
293
294 for conflict in pkg.pkg.conflicts() {
295 self.check_reverse_conflict(pkg.pkg.name(), runtime, conflict, conflicts)
296 }
297 }
298
299 for pkg in self.iter_aur_pkgs() {
300 if runtime && pkg.make {
301 continue;
302 }
303
304 for conflict in pkg.pkg.conflicts.iter() {
305 self.check_reverse_conflict(
306 &pkg.pkg.name,
307 runtime,
308 &Depend::new(conflict.to_string()),
309 conflicts,
310 )
311 }
312 }
313
314 for (_, pkg) in self.iter_pkgbuilds() {
315 if runtime && pkg.make {
316 continue;
317 }
318
319 for conflict in pkg
320 .pkg
321 .conflicts
322 .arch(self.arch())
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 self.install
399 .iter()
400 .map(|pkg| pkg.pkg.name())
401 .chain(build)
402 .chain(pkgbuilds)
403 .filter(|&name| !names.insert(name))
404 .map(Into::into)
405 .collect::<Vec<_>>()
406 }
407}