aur_depends/
resolve.rs

1use crate::actions::{Actions, AurPackage, DepMissing, Missing, RepoPackage, Unneeded};
2use crate::base::Base;
3use crate::cb::{Group, GroupCB, IsDevelCb, ProviderCB};
4use crate::pkgbuild::PkgbuildRepo;
5use crate::satisfies::{satisfies_provide, Satisfies};
6use crate::{AurBase, Error, Pkgbuild, PkgbuildPackages};
7
8use std::collections::HashSet;
9
10use alpm::{Alpm, Dep, Depend, Version};
11use alpm_utils::{AsTarg, DbListExt, Targ};
12use bitflags::bitflags;
13use log::Level::Debug;
14use log::{debug, log_enabled};
15use raur::{ArcPackage, Cache, Raur, SearchBy};
16
17// TODO: pkgbuild repo will not bundle pkg specific deps, which means a package from a srcinfo
18// already in build may not actually already be fully satisfied. check for this and if not push it
19// as a new pkgbase
20
21enum RepoSource {
22    Repo,
23    Pkgbuild,
24    Aur,
25    Unspecified,
26    Missing,
27}
28
29#[derive(Default)]
30struct Targets<'t, 'a> {
31    repo: Vec<(Targ<'t>, &'a alpm::Package)>,
32    pkgbuild: Vec<Targ<'t>>,
33    aur: Vec<&'t str>,
34}
35
36bitflags! {
37    /// Config options for Handle.
38    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
39    pub struct Flags: u32 {
40        /// Do not resolve dependencies.
41        const NO_DEPS = 1 << 2;
42        /// Do not enforse version constraints on dependencies.
43        const NO_DEP_VERSION = 1 << 3;
44        /// Solve provides for targets.
45        const TARGET_PROVIDES = 1 << 4;
46        /// Solve provides for non targets.
47        const NON_TARGET_PROVIDES = 1 << 5;
48        /// Solve provides for missing packages.
49        const MISSING_PROVIDES = 1 << 6;
50        /// Solve provides in all instances.
51        const PROVIDES = 1 << 7;
52        /// Calculate which packages are only needed to build the packages.
53        const CALCULATE_MAKE = 1 << 8;
54        /// Solve checkdepends.
55        const CHECK_DEPENDS = 1 << 10;
56        /// Ignore targets that are up to date.
57        const NEEDED = 1 << 11;
58        /// Search PKGBUILD repos for upgrades and targets
59        const PKGBUILDS = 1 << 12;
60        /// Search aur for targets.
61        const AUR = 1 << 13;
62        /// Search alpm repos for targets.
63        const REPO = 1 << 14;
64        /// when fetching updates, also include packages that are older than locally installed.
65        const ENABLE_DOWNGRADE = 1 << 15;
66        /// Pull in pkgbuild dependencies even if they are already satisfied.
67        const RESOLVE_SATISFIED_PKGBUILDS = 1 << 16;
68    }
69}
70
71impl Flags {
72    /// Create a new Flags with the default configuration
73    pub fn new() -> Self {
74        Flags::CALCULATE_MAKE
75            | Flags::TARGET_PROVIDES
76            | Flags::MISSING_PROVIDES
77            | Flags::CHECK_DEPENDS
78            | Flags::AUR
79            | Flags::REPO
80            | Flags::PKGBUILDS
81    }
82
83    /// Create a new Flags with repo targets disabled
84    pub fn aur_only() -> Self {
85        Flags::new() & !Flags::REPO
86    }
87}
88
89impl Default for Flags {
90    fn default() -> Self {
91        Self::new()
92    }
93}
94
95#[derive(Debug)]
96#[allow(dead_code)]
97enum AurOrPkgbuild<'a> {
98    Aur(&'a raur::Package),
99    Pkgbuild(&'a str, &'a srcinfo::Srcinfo, &'a srcinfo::Package),
100}
101
102impl<'a> AurOrPkgbuild<'a> {
103    fn pkgbase(&self) -> &str {
104        match self {
105            AurOrPkgbuild::Aur(pkg) => &pkg.package_base,
106            AurOrPkgbuild::Pkgbuild(_, base, _) => &base.base.pkgbase,
107        }
108    }
109
110    fn depends(&self, arch: &str, check_depends: bool) -> Vec<&str> {
111        match self {
112            AurOrPkgbuild::Aur(pkg) => {
113                let check = if check_depends {
114                    Some(&pkg.check_depends)
115                } else {
116                    None
117                };
118
119                pkg.make_depends
120                    .iter()
121                    .chain(check.into_iter().flatten())
122                    .chain(&pkg.depends)
123                    .map(|s| s.as_str())
124                    .collect()
125            }
126            AurOrPkgbuild::Pkgbuild(_, base, pkg) => {
127                let base = &base.base;
128                let check = if check_depends {
129                    Some(&base.checkdepends)
130                } else {
131                    None
132                };
133
134                base.makedepends
135                    .iter()
136                    .chain(check.into_iter().flatten())
137                    .chain(&pkg.depends)
138                    .filter(|d| d.arch.is_none() || d.arch.as_deref() == Some(arch))
139                    .flat_map(|d| &d.vec)
140                    .map(|d| d.as_str())
141                    .collect()
142            }
143        }
144    }
145}
146
147/// Resolver is the main type for resolving dependencies
148///
149/// Given a list of targets of either repo or AUR packages it will resolve the dependencies needed
150/// to install them.
151///
152/// This resolver assumes all the repo packages will be installed first, then each base is built
153/// and installed together.
154///
155/// aur-depends will try to solve dependnecies using the minimum ammount of AUR RPC requests.
156///
157/// Resolving is done via the AUR RPC. No packages are downloaded.
158///
159/// # Example
160///
161/// ```no_run
162/// # use aur_depends::Error;
163/// # #[tokio::test]
164/// # async fn run() -> Result<(), Error> {
165/// use std::collections::HashSet;
166/// use alpm::Alpm;
167/// use raur::Handle;
168///
169/// use aur_depends::{Flags, Resolver};
170///
171/// let alpm = Alpm::new("/", "/var/lib/pacman")?;
172/// let raur = Handle::default();
173/// let mut cache = HashSet::new();
174/// let resolver = Resolver::new(&alpm, Vec::new(), &mut cache, &raur, Flags::aur());
175/// let actions = resolver.resolve_targets(&["discord-canary", "spotify"]).await?;
176///
177/// for install in &actions.install {
178///     println!("install: {}", install.pkg.name())
179/// }
180///
181/// for build in actions.iter_build_pkgs() {
182///     println!("build: {}", build.pkg.name)
183/// }
184///
185/// # Ok (())
186/// # }
187/// ```
188#[derive(Debug)]
189pub struct Resolver<'a, 'b, H = raur::Handle> {
190    pub(crate) alpm: &'a Alpm,
191    pub(crate) repos: Vec<PkgbuildRepo<'a>>,
192    pub(crate) resolved: HashSet<String>,
193    pub(crate) cache: &'b mut Cache,
194    pub(crate) stack: Vec<DepMissing>,
195    pub(crate) raur: &'b H,
196    pub(crate) actions: Actions<'a>,
197    pub(crate) seen: HashSet<String>,
198    pub(crate) seen_target: HashSet<String>,
199    pub(crate) flags: Flags,
200    pub(crate) aur_namespace: Option<String>,
201    pub(crate) provider_callback: ProviderCB,
202    pub(crate) group_callback: GroupCB<'a>,
203    pub(crate) is_devel: IsDevelCb,
204}
205
206impl<'a, 'b, E: std::error::Error + Sync + Send + 'static, H: Raur<Err = E> + Sync>
207    Resolver<'a, 'b, H>
208{
209    /// Create a new Resolver
210    pub fn new(alpm: &'a Alpm, cache: &'b mut Cache, raur: &'b H, flags: Flags) -> Self {
211        let actions = Actions {
212            alpm,
213            missing: Vec::new(),
214            unneeded: Vec::new(),
215            build: Vec::new(),
216            install: Vec::new(),
217        };
218
219        Resolver {
220            alpm,
221            repos: Vec::new(),
222            resolved: HashSet::new(),
223            cache,
224            stack: Vec::new(),
225            actions,
226            raur,
227            flags,
228            seen: HashSet::new(),
229            seen_target: HashSet::new(),
230            aur_namespace: None,
231            provider_callback: Default::default(),
232            group_callback: Default::default(),
233            is_devel: Default::default(),
234        }
235    }
236
237    /// If enabled, causes `aur/foo` to mean from the AUR, instead of a repo named `aur`.
238    pub fn aur_namespace(mut self, enable: bool) -> Self {
239        if enable {
240            self.aur_namespace = Some("aur".to_string());
241        }
242        self
243    }
244
245    /// Causes `<name>/foo` to mean from the AUR, instead of a repo named `<name>`.
246    pub fn custom_aur_namespace(mut self, name: Option<String>) -> Self {
247        self.aur_namespace = name;
248        self
249    }
250
251    /// Set the pkgbuild repos to use.
252    pub fn pkgbuild_repos(mut self, repos: Vec<PkgbuildRepo<'a>>) -> Self {
253        self.repos = repos;
254        self
255    }
256
257    /// Getter for the aur cache
258    pub fn get_cache(&self) -> &Cache {
259        self.cache
260    }
261
262    /// Mut getter for the aur cache
263    pub fn get_cache_mut(&mut self) -> &mut Cache {
264        self.cache
265    }
266
267    /// Resolve a list of targets.
268    pub async fn resolve_targets<T: AsTarg>(self, pkgs: &[T]) -> Result<Actions<'a>, Error> {
269        self.resolve(pkgs, &[], true).await
270    }
271
272    /// Resolve a list of dependencies.
273    pub async fn resolve_depends<T: AsRef<str>>(
274        self,
275        deps: &[T],
276        make_deps: &[T],
277    ) -> Result<Actions<'a>, Error> {
278        self.resolve(deps, make_deps, false).await
279    }
280
281    // parse <repo> part of target and figure out where target is
282    fn where_is_target(&self, targ: Targ) -> RepoSource {
283        if let Some(repo) = targ.repo {
284            if self.alpm.syncdbs().into_iter().any(|db| db.name() == repo) {
285                RepoSource::Repo
286            } else if self.repos.iter().any(|r| r.name == repo) {
287                RepoSource::Pkgbuild
288            } else if Some(repo) == self.aur_namespace.as_deref() {
289                RepoSource::Aur
290            } else {
291                RepoSource::Missing
292            }
293        } else {
294            RepoSource::Unspecified
295        }
296    }
297
298    fn split_targets<'t, T: AsTarg>(
299        &mut self,
300        deps: &'t [T],
301        make_deps: &'t [T],
302        is_target: bool,
303    ) -> Targets<'t, 'a> {
304        let mut targets = Targets::default();
305
306        let use_repo = self.flags.contains(Flags::REPO) || !is_target;
307        let use_pkgbuild = self.flags.contains(Flags::PKGBUILDS) || !is_target;
308        let use_aur = self.flags.contains(Flags::AUR) || !is_target;
309
310        'deps: for targ in deps.iter().chain(make_deps) {
311            let targ = targ.as_targ();
312            // TODO
313            // Not handle repo/pkg for !is_target
314
315            let dep = Depend::new(targ.to_string());
316            if !is_target && self.assume_installed(&dep) {
317                continue;
318            }
319
320            let source = self.where_is_target(targ);
321
322            if matches!(source, RepoSource::Repo | RepoSource::Unspecified) && use_repo {
323                if let Some(alpm_pkg) = self.find_repo_target_satisfier(targ) {
324                    targets.repo.push((targ, alpm_pkg));
325                    continue;
326                }
327
328                if is_target {
329                    let groups = self
330                        .alpm
331                        .syncdbs()
332                        .iter()
333                        .filter(|db| targ.repo.is_none() || targ.repo.unwrap() == db.name())
334                        .filter_map(|db| db.group(targ.pkg).map(|group| Group { db, group }).ok())
335                        .collect::<Vec<_>>();
336                    if !groups.is_empty() {
337                        if let Some(f) = self.group_callback.get() {
338                            for alpm_pkg in f(&groups) {
339                                targets.repo.push((targ, alpm_pkg));
340                            }
341                        } else {
342                            for group in groups {
343                                for alpm_pkg in group.group.packages() {
344                                    targets.repo.push((targ, alpm_pkg));
345                                }
346                            }
347                        }
348                        continue;
349                    }
350                }
351            }
352
353            if matches!(source, RepoSource::Pkgbuild | RepoSource::Unspecified) && use_pkgbuild {
354                for repo in &self.repos {
355                    if targ.repo.is_some() && targ.repo != Some(repo.name) {
356                        continue;
357                    }
358
359                    for base in &repo.pkgs {
360                        if let Some(_satisfier) = base.which_satisfies_dep(
361                            &Depend::new(targ.pkg),
362                            self.flags.contains(Flags::NO_DEP_VERSION),
363                        ) {
364                            targets.pkgbuild.push(targ);
365                            continue 'deps;
366                        }
367                    }
368                }
369            }
370
371            if matches!(source, RepoSource::Aur | RepoSource::Unspecified) && use_aur {
372                targets.aur.push(targ.pkg);
373                continue;
374            }
375
376            self.actions.missing.push(Missing {
377                dep: targ.to_string(),
378                stack: Vec::new(),
379            });
380        }
381
382        targets
383    }
384
385    async fn resolve<T: AsTarg>(
386        mut self,
387        deps: &[T],
388        make_deps: &[T],
389        is_target: bool,
390    ) -> Result<Actions<'a>, Error> {
391        let targets = self.split_targets(deps, make_deps, is_target);
392
393        let make = make_deps
394            .iter()
395            .map(|t| t.as_targ().pkg)
396            .collect::<HashSet<&str>>();
397
398        debug!("aur targets are {:?}", targets.aur);
399        debug!("pkgbuild targets are {:?}", targets.pkgbuild);
400
401        self.cache_aur_pkgs_recursive(&targets.aur, &targets.pkgbuild, true)
402            .await?;
403        self.resolved.clear();
404
405        debug!("Caching done, building tree");
406        debug!("cache: {:#?}", self.cache);
407
408        for (pkg, alpm_pkg) in targets.repo {
409            self.resolve_repo_target(pkg, alpm_pkg, &make, is_target)
410        }
411
412        for &pkg in &targets.pkgbuild {
413            self.resolve_pkgbuild_target(pkg, &make, is_target, &targets.aur)?;
414        }
415
416        for &aur_pkg in &targets.aur {
417            self.resolve_aur_target(aur_pkg, &make, is_target, &targets.aur)?;
418        }
419
420        if self.flags.contains(Flags::CALCULATE_MAKE) {
421            self.calculate_make();
422        }
423
424        Ok(self.actions)
425    }
426
427    fn resolve_aur_target(
428        &mut self,
429        aur_pkg: &str,
430        make: &HashSet<&str>,
431        is_target: bool,
432        targs: &[&str],
433    ) -> Result<(), Error> {
434        let dep = Depend::new(aur_pkg);
435        let localdb = self.alpm.localdb();
436
437        self.seen_target.insert(aur_pkg.to_string());
438
439        if self.should_skip_aur_pkg(&dep, is_target) {
440            return Ok(());
441        }
442
443        let pkg = if let Some(pkg) = self.select_satisfier_aur_cache(&dep, is_target) {
444            pkg.clone()
445        } else {
446            self.actions.missing.push(Missing {
447                dep: dep.to_string(),
448                stack: self.stack.clone(),
449            });
450            return Ok(());
451        };
452
453        if self.flags.contains(Flags::NEEDED) || !is_target {
454            let is_devel = self.is_devel.get().map(|f| f(aur_pkg)).unwrap_or(false);
455
456            if !is_devel {
457                if let Ok(local) = localdb.pkg(&*pkg.name) {
458                    if local.version() >= Version::new(&*pkg.version) {
459                        let unneeded =
460                            Unneeded::new(aur_pkg.to_string(), local.version().to_string());
461                        self.actions.unneeded.push(unneeded);
462                        return Ok(());
463                    }
464                }
465            }
466        }
467
468        let is_make = make.contains(&aur_pkg);
469        self.stack
470            .push(DepMissing::new(pkg.name.to_string(), aur_pkg.to_string()));
471        self.resolve_aur_pkg_deps(targs, AurOrPkgbuild::Aur(&pkg), is_make)?;
472        self.stack.pop().unwrap();
473
474        if self.actions.iter_aur_pkgs().any(|p| p.pkg.name == pkg.name) {
475            return Ok(());
476        }
477        if self
478            .actions
479            .iter_pkgbuilds()
480            .any(|p| p.1.pkg.pkgname == pkg.name)
481        {
482            return Ok(());
483        }
484
485        let p = AurPackage {
486            pkg: pkg.clone(),
487            make: false,
488            target: is_target,
489        };
490
491        self.push_aur_build(&pkg.package_base, p);
492        Ok(())
493    }
494
495    fn resolve_repo_target(
496        &mut self,
497        pkg: Targ,
498        alpm_pkg: &'a alpm::Package,
499        make: &HashSet<&str>,
500        is_target: bool,
501    ) {
502        let localdb = self.alpm.localdb();
503
504        if !is_target && localdb.pkgs().find_satisfier(pkg.pkg).is_some() {
505            return;
506        }
507
508        if self.flags.contains(Flags::NEEDED) {
509            if let Ok(local) = localdb.pkg(alpm_pkg.name()) {
510                if local.version() >= alpm_pkg.version() {
511                    let unneeded = Unneeded::new(pkg.to_string(), local.version().to_string());
512                    self.actions.unneeded.push(unneeded);
513                    return;
514                }
515            }
516        }
517
518        let is_make = make.contains(&pkg.pkg);
519
520        self.stack.push(DepMissing::new(
521            alpm_pkg.name().to_string(),
522            pkg.pkg.to_string(),
523        ));
524        self.resolve_repo_pkg(alpm_pkg, is_target, is_make);
525        self.stack.pop().unwrap();
526    }
527
528    fn resolve_pkgbuild_target(
529        &mut self,
530        pkgbuild: Targ,
531        make: &HashSet<&str>,
532        is_target: bool,
533        targs: &[&str],
534    ) -> Result<(), Error> {
535        let dep = Depend::new(pkgbuild.pkg);
536        let localdb = self.alpm.localdb();
537
538        if self.should_skip_aur_pkg(&dep, is_target) {
539            return Ok(());
540        }
541
542        let (repo, base, pkg) =
543            if let Some((repo, base, pkg)) = self.find_pkgbuild_repo_dep(pkgbuild.repo, &dep) {
544                (repo, base, pkg)
545            } else {
546                self.actions.missing.push(Missing {
547                    dep: dep.to_string(),
548                    stack: self.stack.clone(),
549                });
550                return Ok(());
551            };
552
553        if self.flags.contains(Flags::NEEDED) || !is_target {
554            let is_devel = self
555                .is_devel
556                .get()
557                .map(|f| f(pkgbuild.pkg))
558                .unwrap_or(false);
559
560            if !is_devel {
561                if let Ok(local) = localdb.pkg(&*pkg.pkgname) {
562                    if local.version() >= Version::new(base.version()) {
563                        let unneeded =
564                            Unneeded::new(pkgbuild.to_string(), local.version().to_string());
565                        self.actions.unneeded.push(unneeded);
566                        return Ok(());
567                    }
568                }
569            }
570        }
571
572        let base = base.clone();
573        let pkg = pkg.clone();
574        let repo = repo.to_string();
575        let is_make = make.contains(&pkgbuild.pkg);
576        self.stack.push(DepMissing::new(
577            pkg.pkgname.to_string(),
578            pkgbuild.pkg.to_string(),
579        ));
580        self.resolve_aur_pkg_deps(targs, AurOrPkgbuild::Pkgbuild(&repo, &base, &pkg), is_make)?;
581        self.stack.pop().unwrap();
582
583        if self
584            .actions
585            .iter_aur_pkgs()
586            .any(|p| p.pkg.name == pkg.pkgname)
587        {
588            return Ok(());
589        }
590        if self
591            .actions
592            .iter_pkgbuilds()
593            .any(|p| p.1.pkg.pkgname == pkg.pkgname)
594        {
595            return Ok(());
596        }
597
598        let p = Pkgbuild {
599            pkg: pkg.clone(),
600            make: false,
601            target: is_target,
602        };
603
604        self.push_pkgbuild_build(repo.to_string(), base, p);
605        Ok(())
606    }
607
608    fn find_satisfier_aur_cache(&self, dep: &Dep) -> Option<&ArcPackage> {
609        if let Some(pkg) = self
610            .cache
611            .iter()
612            .filter(|pkg| self.alpm.localdb().pkg(pkg.name.as_str()).is_ok())
613            .find(|pkg| pkg.satisfies_dep(dep, self.flags.contains(Flags::NO_DEP_VERSION)))
614        {
615            return Some(pkg);
616        }
617
618        if let Some(pkg) = self.cache.get(dep.name()) {
619            if pkg.satisfies_dep(dep, self.flags.contains(Flags::NO_DEP_VERSION)) {
620                return Some(pkg);
621            }
622        }
623
624        self.cache
625            .iter()
626            .find(|pkg| pkg.satisfies_dep(dep, self.flags.contains(Flags::NO_DEP_VERSION)))
627    }
628
629    /// Expected behaviour
630    /// pull in a list of all matches, if one is installed, default to it.
631    /// unless we are looking for a target, then always show all options.
632    fn select_satisfier_aur_cache(&self, dep: &Dep, target: bool) -> Option<&ArcPackage> {
633        debug!("select satisfier: {}", dep);
634        if let Some(f) = self.provider_callback.get() {
635            let mut pkgs = self
636                .cache
637                .iter()
638                .filter(|pkg| pkg.satisfies_dep(dep, self.flags.contains(Flags::NO_DEP_VERSION)))
639                .map(|pkg| pkg.name.as_str())
640                .collect::<Vec<_>>();
641
642            debug!("satisfiers for '{:?}': {:?})", dep.to_string(), pkgs);
643
644            if !target {
645                if let Some(pkg) = pkgs.iter().find(|&&p| self.alpm.localdb().pkg(p).is_ok()) {
646                    debug!("picked from cache: {}", pkg);
647                    return self.cache.get(*pkg);
648                }
649                if let Some(pkg) = pkgs.iter().find(|&&p| p == dep.name()) {
650                    debug!("picked from cache: {}", pkg);
651                    return self.cache.get(*pkg);
652                }
653            }
654
655            if pkgs.len() == 1 {
656                return self.cache.get(pkgs[0]);
657            } else if pkgs.is_empty() {
658                return None;
659            }
660
661            if !target {
662                for &pkg in &pkgs {
663                    if self.alpm.localdb().pkg(pkg).is_ok() {
664                        return self.cache.get(pkg);
665                    }
666                }
667            }
668
669            if let Some(true_pkg) = pkgs.iter().position(|pkg| *pkg == dep.name()) {
670                pkgs.swap(true_pkg, 0);
671                pkgs[1..].sort_unstable();
672            } else {
673                pkgs.sort_unstable();
674            }
675
676            let choice = f(dep.to_string().as_str(), &pkgs);
677            debug!("choice was: {}={}", choice, pkgs[choice]);
678            self.cache.get(pkgs[choice])
679        } else {
680            debug!("no provider callback");
681            self.find_satisfier_aur_cache(dep)
682        }
683    }
684
685    fn resolve_aur_pkg_deps(
686        &mut self,
687        targs: &[&str],
688        pkg: AurOrPkgbuild,
689        make: bool,
690    ) -> Result<(), Error> {
691        debug!("resolve pkgbuild repo pkg deps: {}", pkg.pkgbase());
692        if !self.flags.contains(Flags::NO_DEPS) {
693            for dep_str in pkg.depends(
694                self.alpm.architectures().first().unwrap_or(""),
695                self.flags.contains(Flags::CHECK_DEPENDS),
696            ) {
697                debug!("depend: {}", dep_str);
698                let dep = Depend::new(dep_str.to_string());
699
700                if self.assume_installed(&dep)
701                    || self.satisfied_build(&dep)
702                    || self.resolved.contains(&dep.to_string())
703                    || self.satisfied_install(&dep)
704                {
705                    continue;
706                }
707
708                let is_aur_targ = self.dep_is_aur_targ(targs, &dep);
709                self.resolved.insert(dep.to_string());
710
711                if !is_aur_targ {
712                    if !self.flags.contains(Flags::RESOLVE_SATISFIED_PKGBUILDS)
713                        && self.satisfied_local(&dep)
714                    {
715                        continue;
716                    }
717                    let depstr = dep.to_string();
718                    if let Some(pkg) = self.find_repo_satisfier(&depstr) {
719                        if !self.satisfied_local(&dep) {
720                            self.stack
721                                .push(DepMissing::new(pkg.name().to_string(), depstr));
722                            self.resolve_repo_pkg(pkg, false, true);
723                            self.stack.pop().unwrap();
724                        }
725                        continue;
726                    }
727                }
728
729                if self.should_skip_aur_pkg(&dep, is_aur_targ) {
730                    continue;
731                }
732
733                if let Some((repo, base, pkg)) = self.find_pkgbuild_repo_dep(None, &dep) {
734                    let repo = repo.to_string();
735                    let base = base.clone();
736                    let pkg = pkg.clone();
737                    self.stack
738                        .push(DepMissing::new(pkg.pkgname.to_string(), dep.to_string()));
739                    self.resolve_aur_pkg_deps(
740                        targs,
741                        AurOrPkgbuild::Pkgbuild(&repo, &base, &pkg),
742                        true,
743                    )?;
744                    self.stack.pop();
745
746                    let pkg = Pkgbuild {
747                        pkg,
748                        make,
749                        target: false,
750                    };
751
752                    self.push_pkgbuild_build(repo.to_string(), base, pkg);
753                    continue;
754                }
755
756                let sat_pkg = if let Some(pkg) = self.select_satisfier_aur_cache(&dep, false) {
757                    pkg.clone()
758                } else {
759                    debug!(
760                        "failed to find '{}' in pkgbuild repo or aur cache",
761                        dep.to_string(),
762                    );
763                    if log_enabled!(Debug) {
764                        debug!(
765                            "at time of failure pkgcache is: {:?}\n",
766                            self.cache.iter().map(|p| &p.name).collect::<Vec<_>>()
767                        );
768                        debug!("stack is: {:?}", self.stack);
769                    }
770
771                    self.actions.missing.push(Missing {
772                        dep: dep.to_string(),
773                        stack: self.stack.clone(),
774                    });
775                    continue;
776                };
777
778                self.stack
779                    .push(DepMissing::new(sat_pkg.name.to_string(), dep.to_string()));
780                self.resolve_aur_pkg_deps(targs, AurOrPkgbuild::Aur(&sat_pkg), true)?;
781                self.stack.pop();
782
783                let p = AurPackage {
784                    pkg: sat_pkg.clone(),
785                    make,
786                    target: false,
787                };
788
789                self.push_aur_build(&sat_pkg.package_base, p);
790            }
791        }
792
793        Ok(())
794    }
795
796    fn find_pkgbuild_repo_dep(
797        &self,
798        repo_targ: Option<&str>,
799        dep: &Depend,
800    ) -> Option<(&str, &srcinfo::Srcinfo, &srcinfo::Package)> {
801        for repo in &self.repos {
802            if repo_targ.is_some() && Some(repo.name) != repo_targ {
803                continue;
804            }
805
806            for base in &repo.pkgs {
807                if let Some(pkg) =
808                    base.which_satisfies_dep(dep, self.flags.contains(Flags::NO_DEP_VERSION))
809                {
810                    return Some((&repo.name, base, base.pkg(pkg).unwrap()));
811                }
812            }
813        }
814        None
815    }
816
817    fn should_skip_aur_pkg(&self, dep: &Depend, is_target: bool) -> bool {
818        if is_target {
819            return false;
820        }
821        if self.flags.contains(Flags::RESOLVE_SATISFIED_PKGBUILDS) {
822            return false;
823        }
824        if self.assume_installed(dep) {
825            return true;
826        }
827        if self.satisfied_local(dep) {
828            return true;
829        }
830
831        false
832    }
833
834    fn resolve_repo_pkg(&mut self, pkg: &'a alpm::Package, target: bool, make: bool) {
835        if !self.seen.insert(pkg.name().to_string()) {
836            return;
837        }
838
839        if !self.flags.contains(Flags::NO_DEPS) {
840            for dep in pkg.depends() {
841                if self.satisfied_install(&dep)
842                    || self.satisfied_local(&dep)
843                    || self.assume_installed(&dep)
844                {
845                    continue;
846                }
847
848                if let Some(pkg) = self.find_repo_satisfier(dep.to_string()) {
849                    self.stack
850                        .push(DepMissing::new(pkg.name().to_string(), dep.to_string()));
851                    self.resolve_repo_pkg(pkg, false, true);
852                    self.stack.pop().unwrap();
853                } else {
854                    self.actions.missing.push(Missing {
855                        dep: dep.to_string(),
856                        stack: self.stack.clone(),
857                    });
858                }
859            }
860        }
861
862        debug!("pushing to install: {}", pkg.name());
863        self.actions.install.push(RepoPackage { pkg, make, target });
864    }
865
866    async fn cache_aur_pkgs<S: AsRef<str>>(
867        &mut self,
868        pkgs: &[S],
869        target: bool,
870    ) -> Result<Vec<ArcPackage>, Error> {
871        let mut pkgs_nover = pkgs
872            .iter()
873            .map(|p| p.as_ref().split(is_ver_char).next().unwrap())
874            .collect::<Vec<_>>();
875        pkgs_nover.sort_unstable();
876        pkgs_nover.dedup();
877
878        if (self.flags.contains(Flags::PROVIDES))
879            || (target && self.flags.contains(Flags::TARGET_PROVIDES))
880        {
881            self.cache_provides(&pkgs_nover).await
882        } else {
883            let mut info = self
884                .raur
885                .cache_info(self.cache, &pkgs_nover)
886                .await
887                .map_err(|e| Error::Raur(Box::new(e)))?;
888
889            if (self.flags.contains(Flags::PROVIDES))
890                || (self.flags.contains(Flags::MISSING_PROVIDES))
891            {
892                let missing = pkgs
893                    .iter()
894                    .map(|pkg| Depend::new(pkg.as_ref()))
895                    .filter(|dep| {
896                        !info.iter().any(|info| {
897                            info.satisfies_dep(dep, self.flags.contains(Flags::NO_DEP_VERSION))
898                        })
899                    })
900                    .map(|dep| dep.to_string())
901                    .collect::<Vec<_>>();
902
903                if !missing.is_empty() {
904                    debug!("attempting to find provides for missing: {:?}", missing);
905                    info.extend(self.cache_provides(&missing).await?);
906                }
907            }
908
909            if log_enabled!(Debug) {
910                debug!(
911                    "provides resolved {:?} found {:?}",
912                    pkgs.iter().map(AsRef::as_ref).collect::<Vec<_>>(),
913                    info.iter().map(|p| p.name.clone()).collect::<Vec<_>>()
914                );
915            }
916            Ok(info)
917        }
918    }
919
920    async fn cache_provides<S: AsRef<str>>(
921        &mut self,
922        pkgs: &[S],
923    ) -> Result<Vec<ArcPackage>, Error> {
924        let mut to_info = pkgs
925            .iter()
926            .map(|s| s.as_ref().to_string())
927            .collect::<Vec<_>>();
928
929        debug!("cache args: {:?}\n", to_info);
930        for pkg in pkgs {
931            let pkg = pkg.as_ref().split(is_ver_char).next().unwrap();
932
933            // Optimization, may break with alpm repos disabled
934            // for example, trying to resolve "pacman" with aur only should pull in
935            // "pacman-git". But because pacman is installed locally, this optimization
936            // causes us to not cache "pacman-git" and end up with missing.
937            //
938            // TODO: maybe check for local && not sync
939            if self.alpm.localdb().pkg(pkg).is_ok() {
940                continue;
941            }
942
943            to_info.extend(
944                self.raur
945                    .search_by(pkg, SearchBy::Provides)
946                    .await
947                    .unwrap_or_else(|e| {
948                        debug!("provide search '{}' failed: {}", pkg, e);
949                        Vec::new()
950                    })
951                    .into_iter()
952                    .map(|p| p.name),
953            );
954        }
955
956        to_info.sort();
957        to_info.dedup();
958
959        debug!("trying to cache {:?}\n", to_info);
960
961        let mut ret = self
962            .raur
963            .cache_info(self.cache, &to_info)
964            .await
965            .map_err(|e| Error::Raur(Box::new(e)))?;
966
967        ret.retain(|pkg| {
968            pkgs.iter().any(|dep| {
969                pkg.satisfies_dep(
970                    &Depend::new(dep.as_ref()),
971                    self.flags.contains(Flags::NO_DEP_VERSION),
972                )
973            })
974        });
975
976        Ok(ret)
977    }
978
979    async fn cache_aur_pkgs_recursive<S: AsRef<str>>(
980        &mut self,
981        pkgs: &[S],
982        pkgbuilds: &[Targ<'_>],
983        target: bool,
984    ) -> Result<(), Error> {
985        let mut new_pkgs = self
986            .cache_aur_pkgs_recursive2(pkgs, pkgbuilds, target)
987            .await?;
988
989        while !new_pkgs.is_empty() {
990            let (pkgbuild_pkgs, pkgs): (Vec<_>, Vec<_>) = new_pkgs.into_iter().partition(|p| {
991                self.find_pkgbuild_repo_dep(None, &Depend::new(p.as_str()))
992                    .is_some()
993            });
994            let pkgbuild_pkgs = pkgbuild_pkgs
995                .iter()
996                .map(|p| Targ {
997                    repo: None,
998                    pkg: p.as_str(),
999                })
1000                .collect::<Vec<_>>();
1001
1002            new_pkgs = self
1003                .cache_aur_pkgs_recursive2(&pkgs, &pkgbuild_pkgs, false)
1004                .await?;
1005        }
1006
1007        Ok(())
1008    }
1009
1010    fn find_aur_deps_of_pkgbuild(&mut self, targ: Targ<'_>) -> Vec<String> {
1011        let mut ret = Vec::new();
1012        if self.flags.contains(Flags::NO_DEPS) {
1013            return Vec::new();
1014        }
1015        let mut new_resolved = HashSet::new();
1016        let mut pkgbuilds = Vec::new();
1017
1018        let pkg = Depend::new(targ.pkg);
1019        if let Some((repo, base, pkg)) = self.find_pkgbuild_repo_dep(targ.repo, &pkg) {
1020            let arch = self.alpm.architectures().first().unwrap_or("");
1021            let pkgbuild = AurOrPkgbuild::Pkgbuild(repo, base, pkg);
1022            let deps = pkgbuild.depends(arch, self.flags.contains(Flags::CHECK_DEPENDS));
1023
1024            for pkg in deps {
1025                let dep = Depend::new(pkg);
1026
1027                if self.resolved.contains(&dep.to_string()) {
1028                    continue;
1029                }
1030                if self.find_repo_satisfier_silent(pkg).is_some() {
1031                    continue;
1032                }
1033                if !self.flags.contains(Flags::RESOLVE_SATISFIED_PKGBUILDS) {
1034                    if self.satisfied_local(&dep) {
1035                        continue;
1036                    }
1037                } else if self.assume_installed(&dep) {
1038                    continue;
1039                }
1040                if self.find_pkgbuild_repo_dep(None, &dep).is_some() {
1041                    pkgbuilds.push(dep.to_depend());
1042                    continue;
1043                }
1044
1045                new_resolved.insert(dep.to_string());
1046                ret.push(pkg.to_string());
1047            }
1048        }
1049
1050        for dep in pkgbuilds {
1051            ret.extend(self.find_aur_deps_of_pkgbuild(Targ::new(None, &dep.to_string())));
1052        }
1053
1054        self.resolved.extend(new_resolved);
1055        ret
1056    }
1057
1058    async fn cache_aur_pkgs_recursive2<S: AsRef<str>>(
1059        &mut self,
1060        pkgs: &[S],
1061        pkgbuild_pkgs: &[Targ<'_>],
1062        target: bool,
1063    ) -> Result<Vec<String>, Error> {
1064        let pkgs = pkgbuild_pkgs
1065            .iter()
1066            .flat_map(|p| self.find_aur_deps_of_pkgbuild(*p))
1067            .chain(pkgs.iter().map(|p| p.as_ref().to_string()))
1068            .collect::<Vec<_>>();
1069
1070        if pkgs.is_empty() {
1071            return Ok(Vec::new());
1072        }
1073
1074        if log_enabled!(Debug) {
1075            debug!(
1076                "cache_aur_pkgs_recursive {:?}",
1077                pkgs.iter().collect::<Vec<_>>()
1078            )
1079        }
1080
1081        let pkgs = self.cache_aur_pkgs(&pkgs, target).await?;
1082        if self.flags.contains(Flags::NO_DEPS) {
1083            return Ok(Vec::new());
1084        }
1085
1086        let mut new_pkgs = Vec::new();
1087        for pkg in pkgs {
1088            let check = if self.flags.contains(Flags::CHECK_DEPENDS) {
1089                Some(&pkg.check_depends)
1090            } else {
1091                None
1092            };
1093
1094            let depends = pkg
1095                .depends
1096                .iter()
1097                .chain(&pkg.make_depends)
1098                .chain(check.into_iter().flatten());
1099
1100            for pkg in depends {
1101                let dep = Depend::new(pkg.as_str());
1102
1103                if self.resolved.contains(&dep.to_string()) {
1104                    continue;
1105                }
1106                if self.find_repo_satisfier_silent(pkg).is_some() {
1107                    continue;
1108                }
1109                if !self.flags.contains(Flags::RESOLVE_SATISFIED_PKGBUILDS) {
1110                    if self.satisfied_local(&dep) {
1111                        continue;
1112                    }
1113                } else if self.assume_installed(&dep) {
1114                    continue;
1115                }
1116
1117                self.resolved.insert(dep.to_string());
1118                new_pkgs.push(pkg.clone());
1119            }
1120        }
1121
1122        Ok(new_pkgs)
1123    }
1124
1125    fn satisfied_build(&self, target: &Dep) -> bool {
1126        for build in &self.actions.build {
1127            match build {
1128                Base::Aur(pkgs) => {
1129                    if pkgs.pkgs.iter().any(|b| {
1130                        b.pkg
1131                            .satisfies_dep(target, self.flags.contains(Flags::NO_DEP_VERSION))
1132                    }) {
1133                        return true;
1134                    }
1135                }
1136                Base::Pkgbuild(pkgs) => {
1137                    if pkgs.pkgs.iter().any(|pkg| {
1138                        (&*pkgs.srcinfo, &pkg.pkg)
1139                            .satisfies_dep(target, self.flags.contains(Flags::NO_DEP_VERSION))
1140                    }) {
1141                        return true;
1142                    }
1143                }
1144            }
1145        }
1146        false
1147    }
1148
1149    fn satisfied_install(&self, target: &Dep) -> bool {
1150        self.actions.install.iter().any(|install| {
1151            install
1152                .pkg
1153                .satisfies_dep(target, self.flags.contains(Flags::NO_DEP_VERSION))
1154        })
1155    }
1156
1157    fn satisfied_local(&self, target: &Dep) -> bool {
1158        if let Ok(pkg) = self.alpm.localdb().pkg(target.name()) {
1159            if pkg.satisfies_dep(target, self.flags.contains(Flags::NO_DEP_VERSION)) {
1160                return true;
1161            }
1162        }
1163
1164        if self.flags.contains(Flags::NO_DEP_VERSION) {
1165            let ret = self.alpm.localdb().pkgs().find_satisfier(target.name());
1166            ret.is_some()
1167        } else {
1168            let ret = self
1169                .alpm
1170                .localdb()
1171                .pkgs()
1172                .find_satisfier(target.to_string());
1173            ret.is_some()
1174        }
1175    }
1176
1177    fn find_repo_target_satisfier(&self, mut target: Targ) -> Option<&'a alpm::Package> {
1178        if self.flags.contains(Flags::NO_DEP_VERSION) {
1179            target = Targ {
1180                repo: target.repo,
1181                pkg: target
1182                    .pkg
1183                    .split_once(is_ver_char)
1184                    .map_or(target.pkg, |x| x.0),
1185            };
1186        }
1187
1188        self.alpm.syncdbs().find_target_satisfier(target)
1189    }
1190
1191    fn find_repo_satisfier<S: AsRef<str>>(&self, target: S) -> Option<&'a alpm::Package> {
1192        let mut target = target.as_ref();
1193
1194        if self.flags.contains(Flags::NO_DEP_VERSION) {
1195            target = target.split_once(is_ver_char).map_or(target, |x| x.0)
1196        }
1197
1198        self.alpm.syncdbs().find_satisfier(target)
1199    }
1200
1201    fn find_repo_satisfier_silent<S: AsRef<str>>(&self, target: S) -> Option<&'a alpm::Package> {
1202        let cb = self.alpm.take_raw_question_cb();
1203        let pkg = self.find_repo_satisfier(target);
1204        self.alpm.set_raw_question_cb(cb);
1205        pkg
1206    }
1207
1208    fn dep_is_aur_targ(&self, targs: &[&str], dep: &Dep) -> bool {
1209        if let Some(pkg) = self.find_satisfier_aur_cache(dep) {
1210            for &targ in targs {
1211                if self.seen_target.contains(targ) {
1212                    continue;
1213                }
1214                if pkg.satisfies_dep(
1215                    &Depend::new(targ),
1216                    self.flags.contains(Flags::NO_DEP_VERSION),
1217                ) {
1218                    return true;
1219                }
1220            }
1221        }
1222
1223        false
1224    }
1225
1226    fn push_aur_build(&mut self, pkgbase: &str, pkg: AurPackage) {
1227        debug!("pushing to build: {}", pkg.pkg.name);
1228        let mut build = true;
1229
1230        if let Some(Base::Aur(base)) = self.actions.build.last_mut() {
1231            if base.package_base() == pkgbase {
1232                base.pkgs.push(pkg);
1233                return;
1234            }
1235        }
1236
1237        for base in self.actions.build.iter_mut() {
1238            if let Base::Aur(pkgs) = base {
1239                if pkgs.pkgs[0].pkg.package_base == pkgbase {
1240                    build = false;
1241                    break;
1242                }
1243            }
1244        }
1245
1246        self.actions.build.push(Base::Aur(AurBase {
1247            pkgs: vec![pkg],
1248            build,
1249        }));
1250    }
1251
1252    // TODO: multiple packages may have same pkgbase
1253    fn push_pkgbuild_build(&mut self, repo: String, base: srcinfo::Srcinfo, pkg: Pkgbuild) {
1254        debug!("pushing to build: {}", pkg.pkg.pkgname);
1255        let mut b = true;
1256
1257        if let Some(Base::Pkgbuild(b)) = self.actions.build.last_mut() {
1258            if b.package_base() == base.base.pkgbase {
1259                b.pkgs.push(pkg);
1260                return;
1261            }
1262        }
1263
1264        for build in self.actions.build.iter_mut() {
1265            if let Base::Pkgbuild(pkgs) = build {
1266                if pkgs.srcinfo.base.pkgbase == base.base.pkgbase {
1267                    b = false;
1268                    break;
1269                }
1270            }
1271        }
1272
1273        self.actions.build.push(Base::Pkgbuild(PkgbuildPackages {
1274            repo,
1275            srcinfo: Box::new(base),
1276            pkgs: vec![pkg],
1277            build: b,
1278        }));
1279    }
1280
1281    pub(crate) fn find_pkgbuild(
1282        &self,
1283        name: &str,
1284    ) -> Option<(&'a str, &'a srcinfo::Srcinfo, &'a srcinfo::Package)> {
1285        for repo in &self.repos {
1286            for &srcinfo in &repo.pkgs {
1287                for pkg in &srcinfo.pkgs {
1288                    if pkg.pkgname == name {
1289                        return Some((repo.name, srcinfo, pkg));
1290                    }
1291                }
1292            }
1293        }
1294        None
1295    }
1296
1297    pub(crate) fn is_pkgbuild(&self, name: &str) -> bool {
1298        self.find_pkgbuild(name).is_some()
1299    }
1300
1301    fn calculate_make(&mut self) {
1302        let mut runtime = Vec::new();
1303        let mut run = true;
1304        let no_dep_ver = self.flags.contains(Flags::NO_DEP_VERSION);
1305        let arch = self.alpm.architectures().first();
1306
1307        self.actions
1308            .install
1309            .iter()
1310            .filter(|p| !p.make)
1311            .for_each(|p| runtime.extend(p.pkg.depends().iter().map(|d| d.to_depend())));
1312        self.actions
1313            .iter_aur_pkgs()
1314            .filter(|p| !p.make)
1315            .for_each(|p| runtime.extend(p.pkg.depends.iter().map(|d| Depend::new(d.as_str()))));
1316        self.actions
1317            .iter_pkgbuilds()
1318            .filter(|p| !p.1.make)
1319            .for_each(|p| {
1320                runtime.extend(
1321                    p.1.pkg
1322                        .depends
1323                        .iter()
1324                        .filter(|d| {
1325                            d.arch.is_none()
1326                                || d.arch.as_deref() == self.alpm.architectures().first()
1327                        })
1328                        .flat_map(|d| &d.vec)
1329                        .map(|d| Depend::new(d.as_str())),
1330                )
1331            });
1332
1333        runtime.sort_unstable_by_key(|a| a.to_string());
1334        runtime.dedup_by_key(|a| a.to_string());
1335
1336        while run {
1337            run = false;
1338            for pkg in &mut self.actions.install {
1339                if !pkg.make {
1340                    continue;
1341                }
1342
1343                let satisfied = runtime
1344                    .iter()
1345                    .any(|dep| pkg.pkg.satisfies_dep(dep, no_dep_ver));
1346
1347                if satisfied {
1348                    pkg.make = false;
1349                    run = true;
1350                    runtime.extend(pkg.pkg.depends().iter().map(|d| d.to_depend()));
1351                }
1352            }
1353
1354            for base in &mut self.actions.build {
1355                match base {
1356                    Base::Aur(base) => {
1357                        for pkg in &mut base.pkgs {
1358                            if !pkg.make {
1359                                continue;
1360                            }
1361
1362                            let satisfied = runtime
1363                                .iter()
1364                                .any(|dep| pkg.pkg.satisfies_dep(dep, no_dep_ver));
1365
1366                            if satisfied {
1367                                pkg.make = false;
1368                                run = true;
1369                                runtime.extend(
1370                                    pkg.pkg.depends.iter().map(|d| Depend::new(d.as_str())),
1371                                );
1372                            }
1373                        }
1374                    }
1375                    Base::Pkgbuild(pkgs) => {
1376                        for pkg in &mut pkgs.pkgs {
1377                            if !pkg.make {
1378                                continue;
1379                            }
1380
1381                            let satisfied = runtime.iter().any(|dep| {
1382                                (&*pkgs.srcinfo, &pkg.pkg).satisfies_dep(dep, no_dep_ver)
1383                            });
1384
1385                            if satisfied {
1386                                pkg.make = false;
1387                                run = true;
1388                                runtime.extend(
1389                                    pkg.pkg
1390                                        .depends
1391                                        .iter()
1392                                        .filter(|d| d.arch.is_none() || d.arch.as_deref() == arch)
1393                                        .flat_map(|d| &d.vec)
1394                                        .map(|d| Depend::new(d.as_str())),
1395                                );
1396                            }
1397                        }
1398                    }
1399                }
1400            }
1401        }
1402    }
1403
1404    fn assume_installed(&self, dep: &Dep) -> bool {
1405        let nover = self.flags.contains(Flags::NO_DEP_VERSION);
1406        self.alpm
1407            .assume_installed()
1408            .iter()
1409            .any(|assume| satisfies_provide(dep, &assume, nover))
1410    }
1411}
1412
1413fn is_ver_char(c: char) -> bool {
1414    matches!(c, '<' | '=' | '>')
1415}
1416
1417#[cfg(test)]
1418mod tests {
1419    use super::*;
1420    use crate::tests::*;
1421    use crate::Conflict;
1422    use alpm::SigLevel;
1423    use simplelog::{ColorChoice, ConfigBuilder, LevelFilter, TermLogger, TerminalMode};
1424
1425    struct TestActions {
1426        build: Vec<String>,
1427        install: Vec<String>,
1428        missing: Vec<Vec<String>>,
1429        make: usize,
1430        duplicates: Vec<String>,
1431        targets: Vec<String>,
1432    }
1433
1434    fn _init_logger() {
1435        let _ = TermLogger::init(
1436            LevelFilter::Trace,
1437            ConfigBuilder::new()
1438                .add_filter_allow_str("aur_depends")
1439                .build(),
1440            TerminalMode::Stderr,
1441            ColorChoice::Never,
1442        );
1443    }
1444
1445    fn alpm() -> Alpm {
1446        //let handle = Alpm::new("/", "/var/lib/pacman/").unwrap();
1447        let mut handle = Alpm::new("/", "tests/db").unwrap();
1448        handle.register_syncdb("core", SigLevel::NONE).unwrap();
1449        handle.register_syncdb("extra", SigLevel::NONE).unwrap();
1450        handle.register_syncdb("community", SigLevel::NONE).unwrap();
1451        handle.register_syncdb("multilib", SigLevel::NONE).unwrap();
1452        handle
1453            .add_assume_installed(&Depend::new("assume-dep1"))
1454            .unwrap();
1455        handle.add_assume_installed(&Depend::new("i3-wm")).unwrap();
1456        handle
1457    }
1458
1459    async fn resolve(pkgs: &[&str], flags: Flags) -> TestActions {
1460        //_init_logger();
1461        let raur = raur();
1462        let alpm = alpm();
1463        let mut cache = HashSet::new();
1464        let srcinfo = srcinfo::Srcinfo::parse_file("tests/srcinfo/custom.SRCINFO").unwrap();
1465        let srcinfo = vec![&srcinfo];
1466
1467        let repo = vec![PkgbuildRepo {
1468            name: "my_repo",
1469            pkgs: srcinfo,
1470        }];
1471
1472        let handle = Resolver::new(&alpm, &mut cache, &raur, flags)
1473            .pkgbuild_repos(repo)
1474            .aur_namespace(true)
1475            .provider_callback(|_, pkgs| {
1476                debug!("provider choice: {:?}", pkgs);
1477                if let Some(i) = pkgs.iter().position(|pkg| *pkg == "yay-bin") {
1478                    i
1479                } else {
1480                    0
1481                }
1482            });
1483
1484        let actions = handle.resolve_targets(pkgs).await.unwrap();
1485
1486        let mut build = actions
1487            .iter_aur_pkgs()
1488            .map(|p| p.pkg.name.clone())
1489            .collect::<Vec<_>>();
1490        build.extend(actions.iter_pkgbuilds().map(|p| p.1.pkg.pkgname.clone()));
1491
1492        let mut install = actions
1493            .install
1494            .iter()
1495            .map(|b| b.pkg.name().to_string())
1496            .collect::<Vec<_>>();
1497
1498        build.sort();
1499        install.sort();
1500
1501        let make = actions.install.iter().filter(|i| i.make).count()
1502            + actions.iter_aur_pkgs().filter(|i| i.make).count()
1503            + actions.iter_pkgbuilds().filter(|i| i.1.make).count();
1504
1505        let mut targets = actions
1506            .iter_aur_pkgs()
1507            .filter(|pkg| pkg.target)
1508            .map(|pkg| pkg.pkg.name.to_string())
1509            .collect::<Vec<_>>();
1510
1511        targets.extend(
1512            actions
1513                .iter_pkgbuilds()
1514                .filter(|pkg| pkg.1.target)
1515                .map(|pkg| pkg.1.pkg.pkgname.to_string()),
1516        );
1517
1518        TestActions {
1519            duplicates: actions.duplicate_targets(),
1520            install,
1521            build,
1522            missing: actions
1523                .missing
1524                .into_iter()
1525                .map(|m| {
1526                    m.stack
1527                        .into_iter()
1528                        .map(|s| s.pkg)
1529                        .chain(Some(m.dep))
1530                        .collect()
1531                })
1532                .collect(),
1533            make,
1534            targets,
1535        }
1536    }
1537
1538    #[tokio::test]
1539    async fn test_yay() {
1540        let TestActions {
1541            install,
1542            build,
1543            make,
1544            ..
1545        } = resolve(&["yay"], Flags::new()).await;
1546
1547        assert_eq!(build, vec!["yay-bin"]);
1548        assert_eq!(
1549            install,
1550            vec!["git", "go", "perl-error", "perl-mailtools", "perl-timedate"]
1551        );
1552        assert_eq!(make, 1);
1553    }
1554
1555    #[tokio::test]
1556    async fn test_yay_needed() {
1557        let TestActions { install, build, .. } =
1558            resolve(&["yay"], Flags::new() | Flags::NEEDED).await;
1559
1560        assert_eq!(build, vec!["yay-bin"]);
1561        assert_eq!(
1562            install,
1563            vec!["git", "go", "perl-error", "perl-mailtools", "perl-timedate"]
1564        );
1565    }
1566
1567    #[tokio::test]
1568    async fn test_yay_no_deps() {
1569        let TestActions { install, build, .. } =
1570            resolve(&["yay"], Flags::new() | Flags::NO_DEPS).await;
1571
1572        assert_eq!(build, vec!["yay-bin"]);
1573        assert_eq!(install, Vec::<String>::new());
1574    }
1575
1576    #[tokio::test]
1577    async fn test_aur_yay_no_deps() {
1578        let TestActions { install, build, .. } =
1579            resolve(&["aur/yay"], Flags::new() | Flags::NO_DEPS).await;
1580
1581        assert_eq!(build, vec!["yay-bin"]);
1582        assert_eq!(install, Vec::<String>::new());
1583    }
1584
1585    #[tokio::test]
1586    async fn test_core_yay_no_deps() {
1587        let TestActions { install, build, .. } =
1588            resolve(&["core/yay"], Flags::new() | Flags::NO_DEPS).await;
1589
1590        assert_eq!(build, Vec::<String>::new());
1591        assert_eq!(install, Vec::<String>::new());
1592    }
1593
1594    #[tokio::test]
1595    async fn test_core_glibc_no_deps() {
1596        let TestActions { install, build, .. } =
1597            resolve(&["core/glibc"], Flags::new() | Flags::NO_DEPS).await;
1598
1599        assert_eq!(build, Vec::<String>::new());
1600        assert_eq!(install, vec!["glibc"]);
1601    }
1602
1603    #[tokio::test]
1604    async fn test_aur_glibc_no_deps() {
1605        let TestActions { install, build, .. } =
1606            resolve(&["core/yay"], Flags::new() | Flags::NO_DEPS).await;
1607
1608        assert_eq!(build, Vec::<String>::new());
1609        assert_eq!(install, Vec::<String>::new());
1610    }
1611
1612    #[tokio::test]
1613    async fn test_extra_glibc_no_deps() {
1614        let TestActions { install, build, .. } =
1615            resolve(&["core/yay"], Flags::new() | Flags::NO_DEPS).await;
1616
1617        assert_eq!(build, Vec::<String>::new());
1618        assert_eq!(install, Vec::<String>::new());
1619    }
1620
1621    #[tokio::test]
1622    async fn test_yay_no_provides() {
1623        let TestActions { install, build, .. } =
1624            resolve(&["yay"], Flags::new() & !Flags::TARGET_PROVIDES).await;
1625
1626        assert_eq!(build, vec!["yay"]);
1627        assert_eq!(
1628            install,
1629            vec!["git", "go", "perl-error", "perl-mailtools", "perl-timedate"]
1630        );
1631    }
1632
1633    #[tokio::test]
1634    async fn test_make_only() {
1635        let TestActions { make, .. } = resolve(
1636            &["ros-melodic-desktop-full"],
1637            Flags::new() & !Flags::TARGET_PROVIDES & !Flags::MISSING_PROVIDES,
1638        )
1639        .await;
1640        assert_eq!(make, 40);
1641    }
1642
1643    #[tokio::test]
1644    async fn test_cache_only() {
1645        let raur = raur();
1646        let alpm = alpm();
1647        let mut cache = HashSet::new();
1648
1649        let mut handle = Resolver::new(&alpm, &mut cache, &raur, Flags::new());
1650        handle
1651            .cache_aur_pkgs_recursive(&["ros-melodic-desktop-full"], &[], true)
1652            .await
1653            .unwrap();
1654    }
1655
1656    #[tokio::test]
1657    async fn test_pacaur() {
1658        let TestActions { install, build, .. } = resolve(&["pacaur"], Flags::new()).await;
1659        assert_eq!(build, vec!["auracle-git", "pacaur"]);
1660        assert_eq!(
1661            install,
1662            vec![
1663                "git",
1664                "jq",
1665                "libnsl",
1666                "meson",
1667                "ninja",
1668                "oniguruma",
1669                "perl-error",
1670                "perl-mailtools",
1671                "perl-timedate",
1672                "python",
1673                "python-appdirs",
1674                "python-packaging",
1675                "python-pyparsing",
1676                "python-setuptools",
1677                "python-six"
1678            ]
1679        );
1680    }
1681
1682    #[tokio::test]
1683    async fn test_wants_pacaur() {
1684        let TestActions { build, .. } = resolve(&["wants-pacaur"], Flags::new()).await;
1685        assert_eq!(build, vec!["wants-pacaur"]);
1686    }
1687
1688    #[tokio::test]
1689    async fn test_wants_pacaur_force_deps() {
1690        let TestActions { build, .. } = resolve(
1691            &["wants-pacaur"],
1692            Flags::new() | Flags::RESOLVE_SATISFIED_PKGBUILDS,
1693        )
1694        .await;
1695        assert_eq!(build, vec!["auracle-git", "pacaur", "wants-pacaur"]);
1696    }
1697
1698    #[tokio::test]
1699    async fn test_pkgbuild() {
1700        let TestActions { install, build, .. } = resolve(&["custom"], Flags::new()).await;
1701        assert_eq!(build, vec!["c1", "c2", "c3", "custom"]);
1702        assert_eq!(install, vec!["libedit", "llvm-libs", "rust"]);
1703    }
1704
1705    #[tokio::test]
1706    async fn test_assume() {
1707        let TestActions { install, build, .. } = resolve(&["assume-test"], Flags::new()).await;
1708        assert_eq!(build, vec!["assume-dep2", "assume-test"]);
1709        assert_eq!(install, vec!["libev"]);
1710    }
1711
1712    #[tokio::test]
1713    async fn test_pacaur_needed() {
1714        let TestActions { install, build, .. } =
1715            resolve(&["pacaur"], Flags::new() | Flags::NEEDED).await;
1716        assert_eq!(build, Vec::<String>::new());
1717        assert_eq!(install, Vec::<String>::new());
1718    }
1719
1720    #[tokio::test]
1721    async fn test_many() {
1722        let TestActions { install, build, .. } = resolve(
1723            &["yay", "pacaur", "pacman", "glibc", "0ad", "spotify"],
1724            Flags::new() | Flags::NO_DEPS,
1725        )
1726        .await;
1727
1728        assert_eq!(build, vec!["pacaur", "spotify", "yay-bin"]);
1729        assert_eq!(install, vec!["0ad", "glibc", "pacman"]);
1730    }
1731
1732    #[tokio::test]
1733    async fn test_many_needed() {
1734        let TestActions { install, build, .. } = resolve(
1735            &["yay", "pacaur", "pacman", "glibc", "0ad", "spotify"],
1736            Flags::new() | Flags::NO_DEPS | Flags::NEEDED,
1737        )
1738        .await;
1739
1740        assert_eq!(build, vec!["spotify", "yay-bin"]);
1741        assert_eq!(install, vec!["0ad", "glibc"]);
1742    }
1743
1744    #[tokio::test]
1745    async fn test_override_dep_via_target() {
1746        let TestActions { install, build, .. } = resolve(&["perl-mailtools"], Flags::new()).await;
1747        assert_eq!(install, ["perl-mailtools", "perl-timedate"]);
1748        assert!(build.is_empty());
1749
1750        let TestActions { install, build, .. } =
1751            resolve(&["perl-mailtools-git"], Flags::new()).await;
1752        assert_eq!(install, ["perl-timedate"]);
1753        assert_eq!(build, ["perl-mailtools-git"]);
1754
1755        let TestActions { install, build, .. } =
1756            resolve(&["perl-timedate-git"], Flags::new()).await;
1757        assert!(install.is_empty());
1758        assert_eq!(build, ["perl-timedate-git"]);
1759
1760        let TestActions { install, build, .. } =
1761            resolve(&["perl-timedate-git", "perl-mailtools-git"], Flags::new()).await;
1762        assert!(install.is_empty());
1763        assert_eq!(build, ["perl-mailtools-git", "perl-timedate-git"]);
1764
1765        let TestActions { install, build, .. } =
1766            resolve(&["perl-mailtools-git", "perl-timedate-git"], Flags::new()).await;
1767        assert!(install.is_empty());
1768        assert_eq!(build, ["perl-mailtools-git", "perl-timedate-git"]);
1769    }
1770
1771    #[tokio::test]
1772    async fn test_a() {
1773        let TestActions { missing, .. } = resolve(&["a"], Flags::new()).await;
1774
1775        assert_eq!(missing, vec![vec!["a", "b>1"]]);
1776    }
1777
1778    #[tokio::test]
1779    async fn test_a_no_ver() {
1780        let TestActions { build, .. } = resolve(&["a"], Flags::new() | Flags::NO_DEP_VERSION).await;
1781
1782        assert_eq!(build, vec!["a", "b"]);
1783    }
1784
1785    #[tokio::test]
1786    async fn test_discord() {
1787        let TestActions {
1788            make,
1789            install,
1790            build,
1791            ..
1792        } = resolve(&["discord-canary"], Flags::new()).await;
1793
1794        println!("{build:?}");
1795        println!("{install:?}");
1796
1797        assert_eq!(build.len(), 3);
1798        assert_eq!(install.len(), 89 + 13);
1799        assert_eq!(make, 9);
1800    }
1801
1802    #[tokio::test]
1803    async fn test_aur_only() {
1804        let TestActions { build, install, .. } =
1805            resolve(&["xterm", "yay"], Flags::aur_only() | Flags::NO_DEPS).await;
1806        assert_eq!(build, vec!["xterm", "yay-bin"]);
1807        assert_eq!(install, Vec::<String>::new());
1808
1809        let TestActions { install, .. } =
1810            resolve(&["pacman"], Flags::aur_only() | Flags::NO_DEPS).await;
1811        assert_eq!(install, Vec::<String>::new());
1812        //1assert_eq!(build, vec!["pacman-git"]);
1813    }
1814
1815    #[tokio::test]
1816    async fn test_repo_only() {
1817        let TestActions { build, install, .. } = resolve(
1818            &["xterm", "yay"],
1819            (Flags::new() | Flags::NO_DEPS) & !Flags::AUR,
1820        )
1821        .await;
1822        assert_eq!(install, vec!["xterm"]);
1823        assert_eq!(build, Vec::<String>::new());
1824
1825        let TestActions { install, build, .. } =
1826            resolve(&["pacman"], (Flags::new() | Flags::NO_DEPS) & !Flags::AUR).await;
1827        assert_eq!(install, vec!["pacman"]);
1828        assert_eq!(build, Vec::<String>::new());
1829    }
1830
1831    #[tokio::test]
1832    async fn test_dups() {
1833        let TestActions {
1834            build,
1835            install,
1836            duplicates,
1837            ..
1838        } = resolve(&["extra/xterm", "aur/xterm"], Flags::new()).await;
1839
1840        println!("{install:#?}");
1841        println!("{build:#?}");
1842        assert_eq!(duplicates.len(), 1);
1843    }
1844
1845    #[tokio::test]
1846    async fn test_inner_conflicts() {
1847        let alpm = alpm();
1848        let raur = raur();
1849        let mut cache = HashSet::new();
1850        let handle = Resolver::new(&alpm, &mut cache, &raur, Flags::new());
1851        let actions = handle
1852            .resolve_targets(&["yay", "yay-git", "yay-bin"])
1853            .await
1854            .unwrap();
1855
1856        let mut conflict1 = Conflict::new("yay".into());
1857        conflict1.push("yay-git".into(), &Depend::new("yay"));
1858        conflict1.push("yay-bin".into(), &Depend::new("yay"));
1859        let mut conflict2 = Conflict::new("yay-bin".into());
1860        conflict2.push("yay-git".into(), &Depend::new("yay"));
1861        let mut conflict3 = Conflict::new("yay-git".into());
1862        conflict3.push("yay-bin".into(), &Depend::new("yay"));
1863
1864        assert_eq!(
1865            actions.calculate_inner_conflicts(true),
1866            vec![conflict1, conflict2, conflict3]
1867        );
1868    }
1869
1870    #[tokio::test]
1871    async fn test_conflicts() {
1872        let alpm = alpm();
1873        let raur = raur();
1874        let mut cache = HashSet::new();
1875        let handle = Resolver::new(&alpm, &mut cache, &raur, Flags::new());
1876        let actions = handle.resolve_targets(&["pacman-git"]).await.unwrap();
1877
1878        let mut conflict = Conflict::new("pacman-git".into());
1879        conflict.push("pacman".into(), &Depend::new("pacman"));
1880
1881        assert_eq!(actions.calculate_conflicts(true), vec![conflict]);
1882    }
1883
1884    #[tokio::test]
1885    async fn test_aur_updates() {
1886        let alpm = alpm();
1887        let raur = raur();
1888        let mut cache = HashSet::new();
1889        let mut handle = Resolver::new(&alpm, &mut cache, &raur, Flags::new());
1890        let pkgs = handle.updates(None).await.unwrap().aur_updates;
1891        let pkgs = pkgs
1892            .iter()
1893            .map(|p| p.remote.name.as_str())
1894            .collect::<Vec<_>>();
1895
1896        assert_eq!(pkgs, vec!["version_newer"]);
1897    }
1898
1899    #[tokio::test]
1900    async fn test_aur_updates_enable_downgrade() {
1901        let alpm = alpm();
1902        let raur = raur();
1903        let mut cache = HashSet::new();
1904        let mut handle = Resolver::new(
1905            &alpm,
1906            &mut cache,
1907            &raur,
1908            Flags::new() | Flags::ENABLE_DOWNGRADE,
1909        );
1910        let pkgs = handle.updates(None).await.unwrap().aur_updates;
1911        let pkgs = pkgs
1912            .iter()
1913            .map(|p| p.remote.name.as_str())
1914            .collect::<Vec<_>>();
1915
1916        assert_eq!(pkgs, vec!["pacaur", "version_newer", "version_older"]);
1917    }
1918
1919    #[tokio::test]
1920    async fn test_repo_nover() {
1921        let TestActions { install, .. } = resolve(&["repo_version_test"], Flags::new()).await;
1922        assert_eq!(install, Vec::<String>::new());
1923
1924        let TestActions { install, .. } =
1925            resolve(&["repo_version_test"], Flags::new() | Flags::NO_DEP_VERSION).await;
1926        assert_eq!(install, vec!["pacman-contrib"]);
1927    }
1928
1929    #[tokio::test]
1930    async fn test_satisfied_versioned_repo_dep() {
1931        let TestActions { missing, .. } =
1932            resolve(&["satisfied_versioned_repo_dep"], Flags::new()).await;
1933        assert_eq!(
1934            missing,
1935            vec![vec!["satisfied_versioned_repo_dep", "pacman>100"]]
1936        );
1937
1938        let TestActions { missing, .. } = resolve(
1939            &["satisfied_versioned_repo_dep"],
1940            Flags::new() | Flags::NO_DEP_VERSION,
1941        )
1942        .await;
1943        assert_eq!(missing, Vec::<Vec<String>>::new());
1944    }
1945
1946    #[tokio::test]
1947    async fn test_satisfied_versioned_repo_dep_nover() {
1948        let TestActions { build, install, .. } = resolve(
1949            &["satisfied_versioned_repo_dep"],
1950            Flags::new() | Flags::NO_DEP_VERSION,
1951        )
1952        .await;
1953        assert_eq!(build, vec!["satisfied_versioned_repo_dep"]);
1954        assert!(install.is_empty());
1955
1956        let TestActions { missing, .. } = resolve(
1957            &["satisfied_versioned_repo_dep"],
1958            Flags::new() | Flags::NO_DEP_VERSION,
1959        )
1960        .await;
1961        assert_eq!(missing, Vec::<Vec<String>>::new());
1962    }
1963
1964    #[tokio::test]
1965    async fn test_cyclic() {
1966        let TestActions { .. } = resolve(&["cyclic"], Flags::new()).await;
1967    }
1968
1969    #[tokio::test]
1970    async fn test_cyclic2() {
1971        let TestActions { .. } = resolve(&["systemd-git"], Flags::new()).await;
1972    }
1973
1974    #[tokio::test]
1975    async fn test_resolve_targets() {
1976        let raur = raur();
1977        //let raur = raur::Handle::default();
1978        let alpm = alpm();
1979        let mut cache = HashSet::new();
1980        let flags = Flags::new() & !Flags::TARGET_PROVIDES & !Flags::MISSING_PROVIDES;
1981
1982        let handle = Resolver::new(&alpm, &mut cache, &raur, flags).provider_callback(|_, pkgs| {
1983            println!("provider choice: {pkgs:?}");
1984            0
1985        });
1986
1987        let actions = handle
1988            .resolve_targets(&["ros-melodic-desktop-full"])
1989            .await
1990            //.resolve_targets(&["yay", "yay-bin", "yay-git"])
1991            //.resolve_targets(&["yay", "pikaur", "pacman", "glibc", "0ad", "spotify"])
1992            //.resolve_targets(&["0ad"])
1993            //.resolve_targets(&["linux-pf"])
1994            //.resolve_targets(&["ros-melodic-desktop-full", "yay"])
1995            //.resolve_targets(&["ignition-common"])
1996            .unwrap();
1997
1998        actions
1999            .iter_aur_pkgs()
2000            .for_each(|p| println!("b {}", p.pkg.name));
2001        actions
2002            .iter_pkgbuilds()
2003            .for_each(|p| println!("c {}", p.1.pkg.pkgname));
2004
2005        actions
2006            .install
2007            .iter()
2008            .for_each(|p| println!("i {}", p.pkg.name()));
2009
2010        actions
2011            .missing
2012            .iter()
2013            .for_each(|m| println!("missing {m:?}"));
2014
2015        actions.calculate_conflicts(true).iter().for_each(|c| {
2016            println!("c {}: ", c.pkg);
2017            c.conflicting
2018                .iter()
2019                .for_each(|c| println!("    {} ({:?})", c.pkg, c.conflict))
2020        });
2021
2022        actions
2023            .calculate_inner_conflicts(true)
2024            .iter()
2025            .for_each(|c| {
2026                println!("c {}: ", c.pkg);
2027                c.conflicting
2028                    .iter()
2029                    .for_each(|c| println!("    {} ({:?})", c.pkg, c.conflict))
2030            });
2031
2032        actions
2033            .unneeded
2034            .iter()
2035            .for_each(|p| println!("u {}", p.name));
2036
2037        actions
2038            .duplicate_targets()
2039            .iter()
2040            .for_each(|p| println!("d {p}"));
2041
2042        println!(
2043            "build: {}",
2044            actions.iter_aur_pkgs().count() + actions.iter_pkgbuilds().count()
2045        );
2046
2047        println!("install: {}", actions.install.len());
2048    }
2049
2050    #[tokio::test]
2051    async fn test_target_flags() {
2052        let TestActions { targets, .. } = resolve(&["discord-canary"], Flags::new()).await;
2053        assert_eq!(targets, vec!["discord-canary"]);
2054    }
2055}