#ifndef PACKAGEMANAGEMENT_PACKAGESOURCE_H #define PACKAGEMANAGEMENT_PACKAGESOURCE_H #include "./package.h" #include #include #include #include #include #include QT_FORWARD_DECLARE_CLASS(QNetworkReply) namespace RepoIndex { class UpgradeLookupResults; class Reply : public QObject { Q_OBJECT public: Reply(QNetworkReply *networkReply); Reply(const QList networkReplies); const QString &error() const; Q_SIGNALS: void resultsAvailable(); private Q_SLOTS: void replyFinished(); private: virtual void processData(QNetworkReply *reply) = 0; protected: QList m_networkReplies; unsigned int m_remainingReplies; QString m_error; }; inline const QString &Reply::error() const { return m_error; } class PackageReply : public Reply { Q_OBJECT public: PackageReply(QNetworkReply *networkReply, const QStringList &requestedPackages, Repository *repo); PackageReply(const QList &networkReplies, const QStringList &requestedPackages, Repository *repo); const QStringList &requestedPackages() const; Repository *repository(); const Repository *repository() const; protected: QStringList m_requestedPackages; Repository *m_repo; }; inline PackageReply::PackageReply(QNetworkReply *networkReply, const QStringList &requestedPackages, Repository *repo) : Reply(networkReply), m_requestedPackages(requestedPackages), m_repo(repo) {} inline PackageReply::PackageReply(const QList &networkReplies, const QStringList &requestedPackages, Repository *repo) : Reply(networkReplies), m_requestedPackages(requestedPackages), m_repo(repo) {} inline const QStringList &PackageReply::requestedPackages() const { return m_requestedPackages; } inline Repository *PackageReply::repository() { return m_repo; } inline const Repository *PackageReply::repository() const { return m_repo; } class SuggestionsReply : public Reply { Q_OBJECT public: SuggestionsReply(QNetworkReply *networkReply, const QString &term, Repository *repo); const Repository *repository() const; QJsonObject suggestions() const; protected: QString m_term; Repository *m_repo; }; inline SuggestionsReply::SuggestionsReply(QNetworkReply *networkReply, const QString &term, Repository *repo) : Reply(networkReply), m_term(term), m_repo(repo) {} inline const Repository *SuggestionsReply::repository() const { return m_repo; } class PackageLoader { public: QFuture &future(); virtual ~PackageLoader(); protected: PackageLoader(); QMutex m_mutex; QFuture m_future; }; inline PackageLoader::PackageLoader() {} inline PackageLoader::~PackageLoader() {} inline QFuture &PackageLoader::future() { return m_future; } /*! * \brief The RepositoryType enum specifies the type of a repository object. */ enum class RepositoryType { AlpmDatabase, /*! The repository is an AlpmDataBase instance. */ UserRepository, /*! The repository is a UserRepository instance. */ Other /*! The repository type is unknown. */ }; /*! * \brief The PackageDetail enum is used to refer to some kind of information about a package. */ enum class PackageDetail { Basics, /*! Basic information about the package such as knowing it exists, its name, version and description. */ Dependencies, /*! The runtime dependencies of the package. */ SourceInfo, /*! The source info such as make dependencies of the package. */ PackageInfo, /*! Information related to a specific package (pkg-file). */ AllAvailable /*! All available package details. */ }; /*! * \brief The PackageDetailAvailability enum describes when some kind of information about a package * becomes available. */ enum class PackageDetailAvailability { Never, /*! The information is not available and can't be requested. */ Immediately, /*! The information is available immediately after the repository object has been constructed. */ Request, /*! The information is available after the requestPackageInfo() method has been called for the package. */ FullRequest /*! The information is available after the requestFullPackageInfo() method has been called for the package. */ }; /*! * \brief The RepositoryUsage enum specifies the usage of a repository. */ enum class RepositoryUsage { None = 0, Sync = 1, /*! The repository is used when synchronizing. */ Search = (1 << 1), /*! The repository is used when searching. */ Install = (1 << 2), /*! The repository is used to install packages. */ Upgrade = (1 << 3), /*! The repository is used to upgrade packages. */ All = (1 << 4) - 1, /*! The reposiotry is used for everything. */ }; constexpr bool operator &(RepositoryUsage lhs, RepositoryUsage rhs) { return (static_cast(lhs) & static_cast(rhs)) != 0; } inline RepositoryUsage &operator |=(RepositoryUsage &lhs, RepositoryUsage rhs) { lhs = static_cast(static_cast(lhs) | static_cast(rhs)); return lhs; } class Repository : public QObject { Q_OBJECT public: ~Repository(); virtual RepositoryType type() const = 0; // general meta data bool isBusy() const; std::uint32_t index() const; const QString &name() const; const QString &description() const; const std::map > &packages() const; std::map > &packages(); const QStringList packageNames() const; RepositoryUsage usage() const; bool isSourceOnly() const; bool isPackageOnly() const; std::map > &groups(); const std::map > &groups() const; Q_SLOT void updateGroups(); const QStringList &serverUrls() const; QStringList &serverUrls(); SignatureLevel sigLevel() const; void setSigLevel(SignatureLevel sigLevel); // gathering data Q_SLOT PackageLoader *init(); void initAsSoonAsPossible(); void asSoonAsPossible(std::function operation); virtual PackageLoader *internalInit(); virtual PackageDetailAvailability requestsRequired(PackageDetail packageDetail = PackageDetail::Basics) const; virtual SuggestionsReply *requestSuggestions(const QString &phrase); virtual PackageReply *requestPackageInfo(const QStringList &packageNames, bool forceUpdate = false); virtual PackageReply *requestFullPackageInfo(const QStringList &packageNames, bool forceUpdate = false); // package search const Package *packageByName(const QString &name) const; Package *packageByName(const QString &name); const Package *packageProviding(const Dependency &dependency) const; Package *packageProviding(const Dependency &dependency); QList packagesProviding(const Dependency &dependency) const; QList packageByFilter(std::function pred); QFuture computeRequiredBy(const QList &relevantRepositories, bool forceUpdate = false); QJsonObject suggestions(const QString &term) const; // upgrade lookup const QList &upgradeSources() const; QList &upgradeSources(); QJsonArray upgradeSourcesJsonArray() const; void checkForUpgrades(UpgradeLookupResults &results) const; void checkForUpgrades(UpgradeLookupResults &results, const QList &upgradeSources) const; // build system const QString &sourcesDirectory() const; void setSourcesDirectory(const QString &dir); const QString &packagesDirectory() const; void setPackagesDirectory(const QString &dir); // JSON serialization QJsonArray packageNamesJsonArray() const; QJsonObject packagesObjectSkeleton() const; QJsonObject basicInfo(bool includeName = false) const; QJsonObject groupInfo() const; // caching bool isCachingUseful() const; void writeToCacheStream(QDataStream &out); void restoreFromCacheStream(QDataStream &in, bool skipOutdated = true); virtual void writeSpecificCacheHeader(QDataStream &out); virtual std::unique_ptr emptyPackage(); virtual void restoreSpecificCacheHeader(QDataStream &in); CppUtilities::TimeSpan maxPackageAge() const; void setMaxPackageAge(CppUtilities::TimeSpan maxPackageAge); void cleanOutdatedPackages(); bool hasOutdatedPackages(); void wipePackages(); // parsing src/pkg info static void parsePkgInfo(const QByteArray &pkgInfo, QString &name, QList > packageInfo); static void parseDescriptions(const QList &descriptions, QString &name, QList > &fields); QList addPackagesFromSrcInfo(const QByteArray &srcInfo, CppUtilities::DateTime timeStamp); Package *addPackageFromDescription(QString name, const QList &descriptions, PackageOrigin origin, CppUtilities::DateTime timeStamp); // thread synchronization QReadWriteLock *lock() const; static const std::uint32_t invalidIndex = static_cast(-1); Q_SIGNALS: /*! * \brief Emitted after initialization has finished. */ void initialized(); /*! * \brief Emitted after required-by computation has finished. */ void requiredByComputed(); /*! * \brief Indicates the repository is not busy anymore; emitted after either initialization or required-by computation has finished. */ void available(); protected Q_SLOTS: void addBusyFlag(); void removeBusyFlag(); private Q_SLOTS: void discardPackageLoader(); protected: explicit Repository(const QString &name, std::uint32_t index = invalidIndex, QObject *parent = nullptr); std::uint32_t m_index; QString m_name; QString m_description; std::map > m_packages; CppUtilities::TimeSpan m_maxPackageAge; RepositoryUsage m_usage; std::map > m_groups; QStringList m_serverUrls; SignatureLevel m_sigLevel; QList m_upgradeSources; QString m_srcDir; QString m_pkgDir; QAtomicInteger m_isBusy; std::unique_ptr m_loader; private: QReadWriteLock m_lock; }; /*! * \brief Returns the suggestions. */ inline QJsonObject SuggestionsReply::suggestions() const { return m_repo->suggestions(m_term); } /*! * \brief Returns an indication whether the repository is busy (either initializing or computing required-by). * * In this case the repository shouldn't be touched until the available() signal is emitted. * * \remarks This method is thread-safe. */ inline bool Repository::isBusy() const { return m_isBusy.load() != 0; } /*! * \brief Returns the index of the repository. * * The index is used to sort the repositories by their occurrence the configuration files. */ inline std::uint32_t Repository::index() const { return m_index; } /*! * \brief Returns the name. */ inline const QString &Repository::name() const { return m_name; } /*! * \brief Returns the description. */ inline const QString &Repository::description() const { return m_description; } /*! * \brief Returns whether the repository only has sources but no built packages. */ inline bool Repository::isSourceOnly() const { return requestsRequired(PackageDetail::PackageInfo) == PackageDetailAvailability::Never; } /*! * \brief Returns whether the repository only has built packages but no sources. */ inline bool Repository::isPackageOnly() const { return requestsRequired(PackageDetail::SourceInfo) == PackageDetailAvailability::Never; } /*! * \brief Returns the packages. */ inline const std::map > &Repository::packages() const { return m_packages; } /*! * \brief Returns the packages. */ inline std::map > &Repository::packages() { return m_packages; } /*! * \brief Returns the package with the specified \a name or nullptr if the package can not be found. * \remarks Ownership remains by this instance. */ inline const Package *Repository::packageByName(const QString &name) const { try { return m_packages.at(name).get(); } catch(const std::out_of_range &) { return nullptr; } } /*! * \brief Returns the package with the specified \a name or nullptr if the package can not be found. * \remarks Ownership remains by this instance. */ inline Package *Repository::packageByName(const QString &name) { try { return m_packages.at(name).get(); } catch(const std::out_of_range &) { return nullptr; } } /*! * \brief Returns the operations the repository is used for. */ inline RepositoryUsage Repository::usage() const { return m_usage; } /*! * \brief Returns the groups the packages of the repository belong to. */ inline std::map > &Repository::groups() { return m_groups; } /*! * \brief Returns the groups the packages of the repository belong to. */ inline const std::map > &Repository::groups() const { return m_groups; } /*! * \brief Returns the server URLs. */ inline const QStringList &Repository::serverUrls() const { return m_serverUrls; } /*! * \brief Returns the server URLs. * \remarks This non-const version is used by the manager to add the servers. */ inline QStringList &Repository::serverUrls() { return m_serverUrls; } /*! * \brief Returns the signature level of the database. */ inline SignatureLevel Repository::sigLevel() const { return m_sigLevel; } /*! * \brief Sets the signature level. */ inline void Repository::setSigLevel(SignatureLevel sigLevel) { m_sigLevel = sigLevel; } /*! * \brief Returns the upgrade sources for the repository. */ inline const QList &Repository::upgradeSources() const { return m_upgradeSources; } /*! * \brief Returns the upgrade sources for the repository. * \remarks This non-const version is used by the manager to add upgrade sources. */ inline QList &Repository::upgradeSources() { return m_upgradeSources; } inline void Repository::checkForUpgrades(UpgradeLookupResults &results) const { checkForUpgrades(results, upgradeSources()); } /*! * \brief Returns the path of the local sources directory. */ inline const QString &Repository::sourcesDirectory() const { return m_srcDir; } /*! * \brief Sets the path of the local sources directory. */ inline void Repository::setSourcesDirectory(const QString &dir) { m_srcDir = dir; } /*! * \brief Returns the path of the local packages directory. */ inline const QString &Repository::packagesDirectory() const { return m_pkgDir; } /*! * \brief Sets the path of the local packages directory. */ inline void Repository::setPackagesDirectory(const QString &dir) { m_pkgDir = dir; } /*! * \brief Returns whether caching this repository is useful. */ inline bool Repository::isCachingUseful() const { switch(requestsRequired(PackageDetail::AllAvailable)) { case PackageDetailAvailability::Request: case PackageDetailAvailability::FullRequest: return true; default: return false; } } /*! * \brief Returns the max package age. * \sa setMaxPackageAge() */ inline CppUtilities::TimeSpan Repository::maxPackageAge() const { return m_maxPackageAge; } /*! * \brief Sets the max package age which is used by the cleanOutdatedPackages() and * the hasOutdatedPackages() method. */ inline void Repository::setMaxPackageAge(CppUtilities::TimeSpan maxPackageAge) { m_maxPackageAge = maxPackageAge; } /*! * \brief Wipes all packages. */ inline void Repository::wipePackages() { m_packages.clear(); m_groups.clear(); } /*! * \brief Returns the read-write lock used for thread-synchronization. */ inline QReadWriteLock *Repository::lock() const { return const_cast(&m_lock); } } // namespace PackageManagement #endif // PACKAGEMANAGEMENT_PACKAGESOURCE_H