diff --git a/libpkg/algo/search.cpp b/libpkg/algo/search.cpp index 8688482..04866a3 100644 --- a/libpkg/algo/search.cpp +++ b/libpkg/algo/search.cpp @@ -183,97 +183,59 @@ std::vector Config::findPackagesProvidingLibrary(const std: return results; } -/*! - * \brief Returns all packages which names matches \a regex. - */ -std::vector Config::findPackages(const std::regex ®ex, std::size_t limit) +void Config::packages(std::string_view dbName, std::string_view dbArch, const std::string &packageName, const DatabaseVisitor &databaseVisitor, + const PackageVisitorConst &visitor) { - auto pkgs = std::vector(); - for (auto &db : databases) { - db.allPackagesByName([&](std::string_view packageName, const std::function &getPackage) { - if (std::regex_match(packageName.begin(), packageName.end(), regex)) { - auto [packageID, package] = getPackage(); - pkgs.emplace_back(db, package, packageID); - } - return pkgs.size() >= limit; - }); + // don't allow to iterate though all packages + if (packageName.empty()) { + return; } - return pkgs; -} - -std::vector Config::findPackages( - const std::function &databasePred, std::string_view term, std::size_t limit) -{ - auto pkgs = std::vector(); for (auto &db : databases) { - if (!databasePred(db)) { + if ((!dbName.empty() && dbName != db.name) || (!dbArch.empty() && dbArch != db.arch) || (databaseVisitor && databaseVisitor(db))) { continue; } - db.allPackagesByName([&](std::string_view packageName, const std::function &getPackage) { - if (packageName.find(term) != std::string_view::npos) { - const auto [packageID, package] = getPackage(); - pkgs.emplace_back(db, package, packageID); - } - return pkgs.size() >= limit; - }); - } - return pkgs; -} - -/*! - * \brief Returns all packages considered "the same" as \a package. - * \remarks See Package::isSame(). - */ -std::vector Config::findPackages(const Package &package, std::size_t limit) -{ - auto pkgs = std::vector(); - for (auto &db : databases) { - if (const auto [id, pkg] = db.findPackageWithID(package.name); pkg && pkg->isSame(package)) { - pkgs.emplace_back(db, pkg, id); - } - if (pkgs.size() >= limit) { - return pkgs; + if (const auto [id, package] = db.findPackageWithID(packageName); package) { + visitor(db, id, package); } } - return pkgs; } -/*! - * \brief Returns all packages \a packagePred returns true for from all databases \a databasePred returns true for. - */ -std::vector Config::findPackages(const std::function &databasePred, - const std::function &packagePred, std::size_t limit) +void Config::packagesByName(const DatabaseVisitor &databaseVisitor, const PackageVisitorByName &visitor) { - auto pkgs = std::vector(); for (auto &db : databases) { - if (!databasePred(db)) { + if (databaseVisitor && databaseVisitor(db)) { continue; } - db.allPackages([&](StorageID packageID, std::shared_ptr &&package) { - if (packagePred(db, *package)) { - pkgs.emplace_back(db, std::move(package), packageID); - } - return pkgs.size() >= limit; - }); + db.allPackagesByName( + [&](std::string_view packageName, const std::function &getPackage) { return visitor(db, packageName, getPackage); }); } - return pkgs; } -/*! - * \brief Returns all packages \a pred returns true for. - */ -std::vector Config::findPackages(const std::function &pred, std::size_t limit) +void Config::providingPackages(const Dependency &dependency, bool reverse, const DatabaseVisitor &databaseVisitor, const PackageVisitorConst &visitor) { - auto pkgs = std::vector(); for (auto &db : databases) { - db.allPackages([&](StorageID packageID, std::shared_ptr &&package) { - if (pred(db, *package)) { - pkgs.emplace_back(db, std::move(package), packageID); - } - return pkgs.size() >= limit; + if (databaseVisitor && databaseVisitor(db)) { + continue; + } + auto visited = std::unordered_set(); + db.providingPackages(dependency, reverse, [&](StorageID packageID, const std::shared_ptr &package) { + return visited.emplace(packageID).second ? visitor(db, packageID, package) : false; + }); + } +} + +void Config::providingPackages( + const std::string &libraryName, bool reverse, const DatabaseVisitor &databaseVisitor, const PackageVisitorConst &visitor) +{ + for (auto &db : databases) { + if (databaseVisitor && databaseVisitor(db)) { + continue; + } + auto visited = std::unordered_set(); + db.providingPackages(libraryName, reverse, [&](StorageID packageID, const std::shared_ptr &package) { + return visited.emplace(packageID).second ? visitor(db, packageID, package) : false; }); } - return pkgs; } } // namespace LibPkg diff --git a/libpkg/data/config.h b/libpkg/data/config.h index ba10c9e..a2edbac 100644 --- a/libpkg/data/config.h +++ b/libpkg/data/config.h @@ -106,6 +106,12 @@ constexpr bool operator&(BuildOrderOptions lhs, BuildOrderOptions rhs) } struct LIBPKG_EXPORT Config : public Lockable, public ReflectiveRapidJSON::BinarySerializable { + using DatabaseVisitor = std::function; + using PackageVisitorMove + = std::function &&)>; // package is invalidated/reused unless moved from!!! + using PackageVisitorConst = std::function &)>; + using PackageVisitorByName = std::function &)>; + explicit Config(); ~Config(); @@ -132,13 +138,15 @@ struct LIBPKG_EXPORT Config : public Lockable, public ReflectiveRapidJSON::Binar std::unordered_map> &runtimeDependencies, DependencySet &missingDependencies, std::unordered_set &visited); - // search for packages - static std::pair parseDatabaseDenotation(std::string_view databaseDenotation); + // database search/creation Database *findDatabase(std::string_view name, std::string_view architecture); Database *findDatabaseFromDenotation(std::string_view databaseDenotation); Database *findOrCreateDatabase(std::string &&name, std::string_view architecture, bool keepLocalPaths = false); Database *findOrCreateDatabase(std::string_view name, std::string_view architecture, bool keepLocalPaths = false); Database *findOrCreateDatabaseFromDenotation(std::string_view databaseDenotation, bool keepLocalPaths = false); + + // packages search + static std::pair parseDatabaseDenotation(std::string_view databaseDenotation); static std::tuple parsePackageDenotation(std::string_view packageDenotation); std::vector findPackages(std::string_view packageDenotation, std::size_t limit = std::numeric_limits::max()); std::vector findPackages( @@ -149,15 +157,14 @@ struct LIBPKG_EXPORT Config : public Lockable, public ReflectiveRapidJSON::Binar std::vector findPackages( const Dependency &dependency, bool reverse = false, std::size_t limit = std::numeric_limits::max()); std::vector findPackagesProvidingLibrary( - const std::string &library, bool reverse = false, std::size_t limit = std::numeric_limits::max()); - std::vector findPackages(const std::regex ®ex, std::size_t limit = std::numeric_limits::max()); - std::vector findPackages(const std::function &databasePred, std::string_view term, - std::size_t limit = std::numeric_limits::max()); - std::vector findPackages(const Package &package, std::size_t limit = std::numeric_limits::max()); - std::vector findPackages(const std::function &databasePred, - const std::function &packagePred, std::size_t limit = std::numeric_limits::max()); - std::vector findPackages( - const std::function &pred, std::size_t limit = std::numeric_limits::max()); + const std::string &library, bool reverse, std::size_t limit = std::numeric_limits::max()); + + // package iteration + void packages(std::string_view dbName, std::string_view dbArch, const std::string &packageName, const DatabaseVisitor &databaseVisitor, + const PackageVisitorConst &visitor); + void packagesByName(const DatabaseVisitor &databaseVisitor, const PackageVisitorByName &visitor); + void providingPackages(const Dependency &dependency, bool reverse, const DatabaseVisitor &databaseVisitor, const PackageVisitorConst &visitor); + void providingPackages(const std::string &libraryName, bool reverse, const DatabaseVisitor &databaseVisitor, const PackageVisitorConst &visitor); std::vector databases; Database aur = Database("aur"); diff --git a/librepomgr/webapi/routes.cpp b/librepomgr/webapi/routes.cpp index 6577dff..b9b74b9 100644 --- a/librepomgr/webapi/routes.cpp +++ b/librepomgr/webapi/routes.cpp @@ -221,19 +221,19 @@ void getPackages(const Params ¶ms, ResponseHandler &&handler) RAPIDJSON_NAMESPACE::Document document(RAPIDJSON_NAMESPACE::kArrayType); RAPIDJSON_NAMESPACE::Document::Array array(document.GetArray()); - const auto pushPackages = [&dbs, &document, &array, &limit](auto &&packages) { - limit -= packages.size(); - for (const auto &package : packages) { - if (!dbs.empty()) { - const auto *const db = std::get(package.db); - const auto dbIterator = dbs.find(db->name); - if (dbIterator == dbs.end() || dbIterator->second.find(db->arch) == dbIterator->second.end()) { - continue; - } - } - ReflectiveRapidJSON::JsonReflector::push(package, array, document.GetAllocator()); - } - }; + const auto visitDb = dbs.empty() ? LibPkg::Config::DatabaseVisitor() : ([&dbs](Database &db) { + const auto dbIterator = dbs.find(db.name); + return dbIterator == dbs.end() || dbIterator->second.find(db.arch) == dbIterator->second.end(); + }); + const auto pushPackage = details + ? LibPkg::Config::PackageVisitorConst([&array, &document, &limit](Database &, LibPkg::StorageID, const std::shared_ptr &pkg) { + ReflectiveRapidJSON::JsonReflector::push(pkg, array, document.GetAllocator()); + return array.Size() >= limit; + }) + : ([&array, &document, &limit](Database &db, LibPkg::StorageID id, const std::shared_ptr &pkg) { + ReflectiveRapidJSON::JsonReflector::push(LibPkg::PackageSearchResult(db, pkg, id), array, document.GetAllocator()); + return array.Size() >= limit; + }); auto aurPackages = std::vector(); auto neededAurPackages = std::vector(); @@ -248,8 +248,8 @@ void getPackages(const Params ¶ms, ResponseHandler &&handler) = LibPkg::Config::parsePackageDenotation(name); // assume names are in the form "repo@arch/pkgname", eg. "core@i686/gcc" const auto &[dbName, dbArch, packageName] = packageDenotation; const auto isDbAur = dbName == "aur"; + auto packageNameStr = std::string(packageName); if (fromAur && (dbName.empty() || isDbAur)) { - auto packageNameStr = std::string(packageName); if (const auto [aurPackageID, aurPackage] = aurDb.findPackageWithID(packageNameStr); aurPackage && (!details || aurPackage->origin != PackageOrigin::AurRpcSearch)) { aurPackages.emplace_back(aurDb, aurPackage, aurPackageID); @@ -259,45 +259,45 @@ void getPackages(const Params ¶ms, ResponseHandler &&handler) --limit; } if (!isDbAur && (!dbs.empty() || !onlyFromAur)) { - auto packages = params.setup.config.findPackages(packageDenotation, limit); - if (details) { - for (const auto &package : packages) { - if (dbs.empty() || dbs.find(std::get(package.db)->name) != dbs.end()) { - ReflectiveRapidJSON::JsonReflector::push(package.pkg, array, document.GetAllocator()); - } - } - limit -= packages.size(); - } else { - pushPackages(std::move(packages)); - } + params.setup.config.packages(dbName, dbArch, packageNameStr, visitDb, pushPackage); } break; } case Mode::NameContains: - pushPackages(params.setup.config.findPackages( - [&dbs, onlyFromAur](const LibPkg::Database &db) { return (dbs.empty() && !onlyFromAur) || dbs.find(db.name) != dbs.end(); }, name, - limit)); + params.setup.config.packagesByName( + visitDb, [&](LibPkg::Database &db, std::string_view packageName, const std::function &getPackage) { + if (packageName.find(name) != std::string_view::npos) { + const auto [packageID, package] = getPackage(); + return pushPackage(db, packageID, package); + } + return false; + }); if (fromAur && !name.empty()) { neededAurPackages.emplace_back(std::move(name)); } break; case Mode::Regex: - // assume names are regexes try { - pushPackages(params.setup.config.findPackages(std::regex(name.data(), name.size()), limit)); + const auto regex = std::regex(name.data(), name.size()); + params.setup.config.packagesByName( + visitDb, [&](LibPkg::Database &db, std::string_view packageName, const std::function &getPackage) { + if (std::regex_match(packageName.begin(), packageName.end(), regex)) { + const auto [packageID, package] = getPackage(); + return pushPackage(db, packageID, package); + } + return false; + }); } catch (const std::regex_error &e) { throw BadRequest(argsToString("regex is invalid: ", e.what())); } break; case Mode::Provides: case Mode::Depends: - // assume names are dependency notation - pushPackages(params.setup.config.findPackages(Dependency::fromString(name), mode == Mode::Depends, limit)); + params.setup.config.providingPackages(Dependency::fromString(name), mode == Mode::Depends, visitDb, pushPackage); break; case Mode::LibProvides: case Mode::LibDepends: - // assume names are "normalized" library names with platform prefix - pushPackages(params.setup.config.findPackagesProvidingLibrary(name, mode == Mode::LibDepends, limit)); + params.setup.config.providingPackages(name, mode == Mode::LibDepends, visitDb, pushPackage); break; default:; }