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