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