diff options
Diffstat (limited to 'src/feature/localquery.nim')
-rw-r--r-- | src/feature/localquery.nim | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/src/feature/localquery.nim b/src/feature/localquery.nim new file mode 100644 index 0000000..b07ec98 --- /dev/null +++ b/src/feature/localquery.nim @@ -0,0 +1,81 @@ +import + algorithm, future, options, sequtils, sets, strutils, tables, + "../args", "../common", "../config", "../format", "../package", "../pacman", "../utils", + "../wrapper/alpm" + +proc handleQueryOrphans*(args: seq[Argument], config: Config): int = + type Package = tuple[name: string, explicit: bool] + + let (installed, alternatives) = withAlpm(config.root, config.db, + newSeq[string](), config.arch, handle, dbs, errors): + for e in errors: printError(config.color, e) + + var installed = initTable[Package, HashSet[PackageReference]]() + var alternatives = initTable[string, HashSet[PackageReference]]() + + for pkg in handle.local.packages: + proc fixProvides(reference: PackageReference): PackageReference = + if reference.constraint.isNone: + (reference.name, reference.description, + some((ConstraintOperation.eq, $pkg.version))) + else: + reference + + let depends = toSeq(pkg.depends.items) + .map(toPackageReference).toSet + let optional = toSeq(pkg.optional.items) + .map(toPackageReference).toSet + let provides = toSeq(pkg.provides.items) + .map(toPackageReference).map(fixProvides).toSet + + installed.add(($pkg.name, pkg.reason == AlpmReason.explicit), + depends + optional) + if provides.len > 0: + alternatives.add($pkg.name, provides) + + (installed, alternatives) + + let providedBy = lc[(y, x.key) | (x <- alternatives.namedPairs, y <- x.value), + tuple[reference: PackageReference, name: string]] + + let installedSeq = lc[x | (x <- installed.pairs), + tuple[package: Package, dependencies: HashSet[PackageReference]]] + let explicit = installedSeq + .filter(t => t.package.explicit) + .map(t => t.package.name) + .toSet + + proc findRequired(results: HashSet[string], check: HashSet[string]): HashSet[string] = + let full = results + check + + let direct = lc[x | (y <- installedSeq, y.package.name in check, + x <- y.dependencies), PackageReference] + + let indirect = lc[x.name | (y <- direct, x <- providedBy, + y.isProvidedBy(x.reference)), string].toSet + + let checkNext = (direct.map(p => p.name).toSet + indirect) - full + if checkNext.len > 0: findRequired(full, checkNext) else: full + + let required = findRequired(initSet[string](), explicit) + let orphans = installedSeq.map(t => t.package.name).toSet - required + + let targets = args.targets.map(t => (if t[0 .. 5] == "local/": t[6 .. ^1] else: t)) + + # Provide similar output for not installed packages + let unknownTargets = targets.toSet - toSeq(installed.keys).map(p => p.name).toSet + let results = if targets.len > 0: + targets.filter(t => t in orphans or t in unknownTargets) + else: + toSeq(orphans.items).sorted(cmp) + + if results.len > 0: + let newArgs = args.filter(arg => not arg.isTarget and + not arg.matchOption((some("t"), "unrequired")) and + not arg.matchOption((some("d"), "deps"))) & + results.map(r => (r, none(string), ArgumentType.target)) + pacmanExec(false, config.color, newArgs) + elif targets.len == 0: + 0 + else: + 1 |