repoindex/alpm/package.cpp

759 lines
26 KiB
C++

#include "./package.h"
#include "./alpmdatabase.h"
#include "./utilities.h"
#include "./repository.h"
#include "./manager.h"
#include <QJsonObject>
#include <QJsonValue>
#include <QJsonArray>
#include <QJsonDocument>
#include <QVariant>
#include <QDataStream>
#include <QStringBuilder>
#include <iostream>
using namespace std;
using namespace ChronoUtilities;
namespace RepoIndex {
/*!
* \class The Package class holds meta information about an
* Arch Linux package.
*/
/*!
* \brief Constructs a new package instance.
*
* Since it is intenced to use the Package class as base class only,
* this constructor is protected.
*/
Package::Package(const QString &name, Repository *repository) :
m_origin(PackageOrigin::Unknown),
m_repository(repository),
m_timeStamp(DateTime::now()),
m_hasGeneralInfo(false),
m_hasAllGeneralInfo(false),
m_name(name),
m_requiredByComputed(false),
m_hasInstallScript(false),
m_hasBuildRelatedMetaData(false),
m_hasInstallRelatedMetaData(false),
m_validationMethods(ALPM_PKG_VALIDATION_UNKNOWN),
m_installReason(ALPM_PKG_REASON_EXPLICIT),
m_id(-1),
m_categoryId(-1),
m_votes(-1)
{
// initialization must be done in derived class
}
/*!
* \brief Writes the package-type-specific cache header.
*/
void Package::writeSpecificCacheHeader(QDataStream &out)
{
Q_UNUSED(out)
}
/*!
* \brief Restores the package-type-specific cache header.
*/
void Package::restoreSpecificCacheHeader(QDataStream &in)
{
Q_UNUSED(in)
}
/*!
* \brief Destroys the package.
*/
Package::~Package()
{}
/*!
* \brief Computes required-by and optional-for.
*/
void Package::computeRequiredBy(Manager &manager)
{
if(m_requiredByComputed) {
m_requiredBy.clear();
m_optionalFor.clear();
}
switch(origin()) {
case PackageOrigin::File:
case PackageOrigin::LocalDb:
for(const auto &pkgEntry : manager.localDataBase()->packages()) {
for(const auto &dep : pkgEntry.second->dependencies()) {
if(dep.name == m_name) {
m_requiredBy << pkgEntry.first;
break;
}
}
for(const auto &dep : pkgEntry.second->optionalDependencies()) {
if(dep.name == m_name) {
m_optionalFor << pkgEntry.first;
break;
}
}
}
break;
case PackageOrigin::SyncDb:
for(const auto &dbEntry : manager.syncDatabases()) {
for(const auto &pkgEntry : dbEntry.second->packages()) {
for(const auto &dep : pkgEntry.second->dependencies()) {
if(dep.name == m_name) {
m_requiredBy << pkgEntry.first;
break;
}
}
for(const auto &dep : pkgEntry.second->optionalDependencies()) {
if(dep.name == m_name) {
m_optionalFor << pkgEntry.first;
break;
}
}
}
}
break;
default:
; // can not compute this for packages from other sources
}
m_requiredByComputed = true;
}
/*!
* \brief Checks whether the specified \a name and the specified \a version match the specified \a dependency.
*/
bool Package::matches(const QString &name, const QString &version, const Dependency &dependency)
{
if(name == dependency.name) {
PackageVersionComparsion cmp;
switch(dependency.mode) {
case ALPM_DEP_MOD_ANY:
return true;
case ALPM_DEP_MOD_EQ:
return PackageVersion(version).compare(PackageVersion(dependency.version)) == PackageVersionComparsion::Equal;
case ALPM_DEP_MOD_GE:
return (cmp = PackageVersion(version).compare(PackageVersion(dependency.version))) == PackageVersionComparsion::Equal || cmp == PackageVersionComparsion::NewerThenSyncVersion;
case ALPM_DEP_MOD_LE:
return (cmp = PackageVersion(version).compare(PackageVersion(dependency.version))) == PackageVersionComparsion::Equal || cmp == PackageVersionComparsion::PackageUpgradeOnly || cmp == PackageVersionComparsion::SoftwareUpgrade;
case ALPM_DEP_MOD_GT:
return PackageVersion(version).compare(PackageVersion(dependency.version)) == PackageVersionComparsion::NewerThenSyncVersion;
case ALPM_DEP_MOD_LT:
return (cmp = PackageVersion(version).compare(PackageVersion(dependency.version))) == PackageVersionComparsion::PackageUpgradeOnly || cmp == PackageVersionComparsion::SoftwareUpgrade;
default:
;
}
}
return false;
}
/*!
* \brief Checks whether the package matches the specified \a dependency.
*/
bool Package::matches(const Dependency &dependency)
{
// check whether package matches "directly"
if(matches(name(), version(), dependency)) {
return true;
} else {
// check whether at least one of the provides matches
for(const auto &provide : provides()) {
if(matches(provide.name, provide.version, dependency)) {
return true;
}
}
return false;
}
}
/*!
* \cond
*/
namespace Utilities {
inline void put(QJsonObject &obj, const QString &key, const QJsonValue &value)
{
obj.insert(key, value);
}
inline void put(QJsonObject &obj, const QString &key, const DateTime dateTime)
{
if(!dateTime.isNull()) {
put(obj, key, QString::fromLocal8Bit(dateTime.toString().data()));
}
}
inline void put(QJsonObject &obj, const QString &key, const QStringList &values)
{
put(obj, key, QJsonArray::fromStringList(values));
}
void put(QJsonObject &obj, const QString &key, const QList<Dependency> &dependencies)
{
QJsonArray jsonArray;
for(const auto &dependency : dependencies) {
jsonArray << dependency.toJson();
}
put(obj, key, jsonArray);
}
QDataStream &operator <<(QDataStream &out, const QList<Dependency> dependencies)
{
out << static_cast<quint32>(dependencies.count());
for(const auto &dependency : dependencies) {
out << dependency.name << dependency.version << static_cast<quint32>(dependency.mode);
}
return out;
}
QDataStream &operator >>(QDataStream &in, QList<Dependency> &dependencies)
{
quint32 size;
in >> size;
for(quint32 i = 0; i < size; ++i) {
QString name;
in >> name;
QString version;
in >> version;
quint32 mode;
in >> mode;
dependencies << Dependency(name, version, static_cast<_alpm_depmod_t>(mode));
}
return in;
}
inline QDataStream &operator <<(QDataStream &out, const DateTime dateTime)
{
return out << static_cast<quint64>(dateTime.totalTicks());
}
inline QDataStream &operator >>(QDataStream &in, DateTime &dateTime)
{
quint64 ticks;
in >> ticks;
dateTime = DateTime(ticks);
return in;
}
QDataStream &operator <<(QDataStream &out, const map<QString, QByteArray> fileMap)
{
out << static_cast<quint32>(fileMap.size());
for(const auto &entry : fileMap) {
out << entry.first << entry.second;
}
return out;
}
QDataStream &operator >>(QDataStream &in, map<QString, QByteArray> &fileMap)
{
quint32 size;
in >> size;
for(quint32 i = 0; i < size; ++i) {
QString path;
in >> path;
QByteArray data;
in >> data;
fileMap.emplace(path, data);
}
return in;
}
QDataStream &operator <<(QDataStream &out, const QJsonArray &jsonArray)
{
QJsonDocument doc;
doc.setArray(jsonArray);
out << doc.toBinaryData();
return out;
}
QDataStream &operator >>(QDataStream &in, QJsonArray &jsonArray)
{
QByteArray data;
in >> data;
QJsonDocument doc = QJsonDocument::fromBinaryData(data);
jsonArray = doc.array();
return in;
}
}
/*!
* \endcond
*/
using namespace Utilities;
/*!
* \brief Returns basic information about the packages as JSON object.
*/
QJsonObject Package::basicInfo(bool includeRepoAndName) const
{
QJsonObject info;
if(includeRepoAndName) {
if(repository()) {
put(info, QStringLiteral("repo"), repository()->name());
}
put(info, QStringLiteral("name"), name());
}
put(info, QStringLiteral("ver"), version());
put(info, QStringLiteral("desc"), description());
put(info, QStringLiteral("flagdate"), outOfDate());
put(info, QStringLiteral("arch"), buildArchitecture());
put(info, QStringLiteral("bdate"), buildDate());
put(info, QStringLiteral("archs"), architectures());
return info;
}
/*!
* \brief Returns full information about the package as JSON object.
*/
QJsonObject Package::detailedInfo() const
{
QJsonObject info;
put(info, QStringLiteral("buildAvail"), hasBuildRelatedMetaData());
put(info, QStringLiteral("srcAvail"), hasSourceRelatedMetaData());
put(info, QStringLiteral("idate"), installDate());
put(info, QStringLiteral("isize"), QJsonValue(static_cast<long long int>(installedSize())));
put(info, QStringLiteral("url"), upstreamUrl());
put(info, QStringLiteral("lic"), licenses());
put(info, QStringLiteral("grp"), groups());
put(info, QStringLiteral("prov"), provides());
put(info, QStringLiteral("optd"), optionalDependencies());
put(info, QStringLiteral("deps"), dependencies());
put(info, QStringLiteral("requ"), requiredBy());
put(info, QStringLiteral("optf"), optionalFor());
put(info, QStringLiteral("conf"), conflicts());
put(info, QStringLiteral("repl"), replaces());
put(info, QStringLiteral("pack"), packer());
put(info, QStringLiteral("expl"), QJsonValue(installReason() == ALPM_PKG_REASON_EXPLICIT));
put(info, QStringLiteral("scri"), QJsonValue(hasInstallScript()));
put(info, QStringLiteral("sig"), Utilities::validationMethodsStrings(validationMethods()));
put(info, QStringLiteral("file"), fileName());
put(info, QStringLiteral("files"), files());
return info;
}
QJsonObject Package::simpleInfo() const
{
QJsonObject info;
put(info, QStringLiteral("name"), name());
put(info, QStringLiteral("ver"), version());
put(info, QStringLiteral("desc"), description());
put(info, QStringLiteral("flagdate"), outOfDate());
put(info, QStringLiteral("arch"), buildArchitecture());
put(info, QStringLiteral("bdate"), buildDate());
put(info, QStringLiteral("url"), upstreamUrl());
put(info, QStringLiteral("lic"), licenses());
put(info, QStringLiteral("packer"), packer());
return info;
}
/*!
* \brief Writes the package contents to the specified data stream.
*/
void Package::writeToCacheStream(QDataStream &out)
{
out << static_cast<qint32>(m_origin) << m_timeStamp;
// general info
out << m_hasGeneralInfo << m_hasAllGeneralInfo << m_name << m_version << m_description << m_upstreamUrl << m_licenses
<< m_groups << m_dependencies << m_optionalDependencies << m_conflicts << m_provides
<< m_replaces << m_requiredByComputed << m_requiredBy << m_optionalFor << m_hasInstallScript;
// build related meta data
out << m_hasBuildRelatedMetaData << m_fileName << m_files << m_buildDate << m_packer
<< m_md5 << m_sha256 << m_buildArchitecture << m_packageSize << m_makeDependencies;
// installation related meta data
out << m_hasInstallRelatedMetaData << m_installDate << m_installedSize << m_backupFiles
<< static_cast<qint32>(m_validationMethods) << static_cast<qint32>(m_installReason);
// source related meta data
out << m_hasSourceRelatedMetaData << m_baseName << m_architectures << m_id
<< m_categoryId << m_votes << m_outOfDate
<< m_maintainer << m_firstSubmitted << m_lastModified << m_tarUrl << m_sourceFiles;
// write specific header
auto headerStart = out.device()->pos();
out.device()->seek(headerStart + 4);
writeSpecificCacheHeader(out);
auto headerEnd = out.device()->pos();
out.device()->seek(headerStart);
out << static_cast<quint32>(headerEnd - headerStart - 4);
out.device()->seek(headerEnd);
// no extended header
out << static_cast<quint32>(0);
}
void Package::restoreFromCacheStream(QDataStream &in)
{
qint32 tmp;
// origin
in >> tmp;
m_origin = static_cast<PackageOrigin>(tmp); // TODO: validate value
in >> m_timeStamp;
// general info
in >> m_hasGeneralInfo >> m_hasAllGeneralInfo >> m_name >> m_version >> m_description >> m_upstreamUrl >> m_licenses
>> m_groups >> m_dependencies >> m_optionalDependencies >> m_conflicts >> m_provides
>> m_replaces >> m_requiredByComputed >> m_requiredBy >> m_optionalFor >> m_hasInstallScript;
// build related meta data
in >> m_hasBuildRelatedMetaData >> m_fileName >> m_files >> m_buildDate >> m_packer
>> m_md5 >> m_sha256 >> m_buildArchitecture >> m_packageSize >> m_makeDependencies;
// installation related meta data
in >> m_hasInstallRelatedMetaData >> m_installDate >> m_installedSize >> m_backupFiles;
in >> tmp;
m_validationMethods = static_cast<alpm_pkgvalidation_t>(m_validationMethods); // TODO: validate value
in >> tmp;
m_installReason = static_cast<alpm_pkgreason_t>(m_installReason); // TODO: validate value
// source related meta data
in >> m_hasSourceRelatedMetaData >> m_baseName >> m_architectures >> m_id
>> m_categoryId >> m_votes >> m_outOfDate
>> m_maintainer >> m_firstSubmitted >> m_lastModified >> m_tarUrl >> m_sourceFiles;
// specific header
quint32 headerSize;
in >> headerSize;
quint64 headerEnd = in.device()->pos() + headerSize;
restoreSpecificCacheHeader(in);
in.device()->seek(headerEnd);
if(in.status() == QDataStream::Ok) {
// skip extended header
in >> headerSize;
quint64 headerEnd = in.device()->pos() + headerSize;
in.device()->seek(headerEnd);
}
}
/*!
* \brief Puts the specified src/pkg info key value pairs; clears current dependencies, provides, ... before inserting new values.
* \remarks This method should only be called by the associated repository because it must handle a possible name change.
*/
void Package::putInfo(const QList<QPair<QString, QString> > &baseInfo, const QList<QPair<QString, QString> > &pkgInfo, bool includesSourceRelatedMetaData)
{
// clear current values
m_licenses.clear();
m_dependencies.clear();
m_makeDependencies.clear();
m_checkDependencies.clear();
m_optionalDependencies.clear();
m_conflicts.clear();
m_provides.clear();
m_replaces.clear();
// read specified key value pairs
PackageVersion version;
QStringList archs;
const auto infos = {baseInfo, pkgInfo};
for(const auto &info : infos) {
for(const auto &pair : info) {
const auto &field = pair.first;
const auto &value = pair.second;
if(field == QLatin1String("pkgbase")) {
m_baseName = value;
} else if(field == QLatin1String("pkgname")) {
m_name = value;
} else if(field == QLatin1String("epoch")) {
version.epoch = value;
} else if(field == QLatin1String("pkgver")) {
version.version = value;
} else if(field == QLatin1String("pkgrel")) {
version.release = value;
} else if(field == QLatin1String("pkgdesc")) {
m_description = value;
} else if(field == QLatin1String("url")) {
m_upstreamUrl = value;
} else if(field == QLatin1String("arch")) {
archs << value;
} else if(field == QLatin1String("license")) {
m_licenses << value;
} else if(field == QLatin1String("depends")) {
m_dependencies << Dependency(value);
} else if(field == QLatin1String("makedepends")) {
m_makeDependencies << Dependency(value);
} else if(field == QLatin1String("checkdepends")) {
m_checkDependencies << Dependency(value);
} else if(field == QLatin1String("optdepends")) {
m_optionalDependencies << Dependency(value);
} else if(field == QLatin1String("conflicts")) {
m_conflicts << Dependency(value);
} else if(field == QLatin1String("provides")) {
m_provides << Dependency(value);
} else if(field == QLatin1String("replaces")) {
m_replaces << Dependency(value);
} else if(field == QLatin1String("source")) {
// currently not used
}
}
}
if(!version.version.isEmpty()) {
m_version = version.toString();
}
if(!archs.isEmpty()) {
m_architectures.swap(archs);
}
// consider general information as complete
m_hasGeneralInfo = m_hasAllGeneralInfo = true;
m_hasSourceRelatedMetaData = includesSourceRelatedMetaData;
}
void Package::putSourceFile(const QString &path, const QByteArray &data)
{
m_sourceFiles.insert(make_pair(path, data));
}
/*!
* \brief The PackageVersion class helps parsing package versions.
*/
/*!
* \brief Constructs a new PackageVersion instance from the specified \a versionStr.
*/
PackageVersion::PackageVersion(const QString &versionStr)
{
// determine start offsets of version and release
const ushort *str = versionStr.utf16(), *versionBeg = nullptr, *releaseBeg = nullptr;
for(const auto *i = str; ; ++i) {
switch(*i) {
case 0:
goto terminationFound;
case ':':
if(!versionBeg && !releaseBeg) {
versionBeg = i + 1;
}
break;
case '-':
releaseBeg = i + 1;
break;
default: ;
}
}
terminationFound:
if(versionBeg) {
// epoch present
epoch = QString::fromUtf16(str, versionBeg - str - 1);
if(releaseBeg) {
// release present
version = QString::fromUtf16(versionBeg, releaseBeg - versionBeg - 1);
release = QString::fromUtf16(releaseBeg);
} else {
version = QString::fromUtf16(versionBeg);
release = QStringLiteral("1");
}
} else {
// epoch not present
epoch = QStringLiteral("0");
if(releaseBeg) {
// release present
version = QString::fromUtf16(str, releaseBeg - str - 1);
release = QString::fromUtf16(releaseBeg);
} else {
version = QString::fromUtf16(str);
release = QStringLiteral("1");
}
}
}
/*!
* \brief Constructs an empty package version.
*/
PackageVersion::PackageVersion()
{}
/*!
* \brief Returns the string representation of the package version: epoch:version-release
*/
QString RepoIndex::PackageVersion::toString() const
{
if(epoch.isEmpty()) {
return version % QChar('-') % (release.isEmpty() ? QStringLiteral("1") : release);
} else {
return epoch % QChar(':') % version % QChar('-') % (release.isEmpty() ? QStringLiteral("1") : release);
}
}
/*!
* \brief Compares two version parts.
* \returns Returns 1 if part1 is newer then part2, -1 if part2 is newer then part1 and 0 if both parts are equal.
*/
PackageVersionPartComparsion PackageVersion::compareParts(const QString &part1, const QString &part2)
{
static const QRegExp nonAlphanumericPattern(QStringLiteral("[^a-zA-Z\\d:öäüÖÄÜß]"));
int part1Pos = 0, part2Pos = 0, part1End, part2End;
while(true) {
// determine current segments
part1End = part1.indexOf(nonAlphanumericPattern, part1Pos);
part2End = part2.indexOf(nonAlphanumericPattern, part2Pos);
auto segment1 = part1.midRef(part1Pos, part1End >= 0 ? part1End - part1Pos : -1);
auto segment2 = part2.midRef(part2Pos, part2End >= 0 ? part2End - part2Pos : -1);
// compare current segments
int digit1 = segment1.size();
int digit2 = segment2.size();
const auto *i1 = segment1.cbegin(), *e1 = segment1.cend();
const auto *i2 = segment2.cbegin(), *e2 = segment2.cend();
// trim leading zeros
for(; i1 < e1 && *i1 == QLatin1Char('0'); ++i1, --digit1);
for(; i2 < e2 && *i2 == QLatin1Char('0'); ++i2, --digit2);
if(digit1 > digit2) {
// segment 1 has more digits -> newer
return PackageVersionPartComparsion::Newer;
} else if (digit2 > digit1) {
// segment 2 has more digits -> newer
return PackageVersionPartComparsion::Older;
} else {
for(; i1 < e1 && i2 < e2; ++i1, ++i2) {
if(*i1 > *i2) {
// segment 1 digit has higher value -> newer
return PackageVersionPartComparsion::Newer;
} else if(*i2 > *i1) {
// segment 2 digit has higher value -> newer
return PackageVersionPartComparsion::Older;
}
}
}
// this segment is equal, look for the next segment
if(part1End >= 0 && part2End >= 0) {
// there is another segment in both parts
part1Pos = part1End + 1;
part2Pos = part2End + 1;
} else if(part1End >= 0) {
// only part 1 has another segment -> it is more specific and hence considered newer
return PackageVersionPartComparsion::Newer;
} else if(part2End >= 0) {
// only part 2 has another segment -> it is more specific and hence considered newer
return PackageVersionPartComparsion::Older;
} else {
// none of the parts has another segment -> parts are equal
return PackageVersionPartComparsion::Equal;
}
}
}
/*!
* \brief Compares this version with another version.
*
* This method distinguishes between software upgrades and package releases. See Alpm::PackageVersionComparsion enum.
*/
PackageVersionComparsion PackageVersion::compare(const PackageVersion &other) const
{
if(!epoch.isEmpty() || !other.epoch.isEmpty()) {
switch(compareParts(other.epoch, epoch)) {
case PackageVersionPartComparsion::Newer: return PackageVersionComparsion::SoftwareUpgrade;
case PackageVersionPartComparsion::Older: return PackageVersionComparsion::NewerThenSyncVersion;
case PackageVersionPartComparsion::Equal: ;
}
}
switch(compareParts(other.version, version)) {
case PackageVersionPartComparsion::Newer: return PackageVersionComparsion::SoftwareUpgrade;
case PackageVersionPartComparsion::Older: return PackageVersionComparsion::NewerThenSyncVersion;
case PackageVersionPartComparsion::Equal: ;
}
switch(compareParts(other.release, release)) {
case PackageVersionPartComparsion::Newer: return PackageVersionComparsion::PackageUpgradeOnly;
case PackageVersionPartComparsion::Older: return PackageVersionComparsion::NewerThenSyncVersion;
case PackageVersionPartComparsion::Equal: ;
}
return PackageVersionComparsion::Equal;
}
/*!
* \brief Constructs a dependency from the specified string.
* \remarks \a dependency might have version suffix.
*/
Dependency::Dependency(const QString &dependency)
{
int descrBeg = dependency.lastIndexOf(QChar(':'));
QStringRef actualDependency;
if(descrBeg > 0) {
description = dependency.midRef(descrBeg + 1).trimmed().toString();
actualDependency = dependency.midRef(0, descrBeg);
} else {
actualDependency = QStringRef(&dependency);
}
int suffixBeg;
if((suffixBeg = actualDependency.lastIndexOf(QLatin1String(">="))) > 0) {
mode = ALPM_DEP_MOD_GE;
} else if((suffixBeg = actualDependency.lastIndexOf(QLatin1String("<="))) > 0) {
mode = ALPM_DEP_MOD_LE;
} else if((suffixBeg = actualDependency.lastIndexOf(QChar('='))) > 0) {
mode = ALPM_DEP_MOD_EQ;
} else if((suffixBeg = actualDependency.lastIndexOf(QChar('<'))) > 0) {
mode = ALPM_DEP_MOD_LT;
} else if((suffixBeg = actualDependency.lastIndexOf(QChar('>'))) > 0) {
mode = ALPM_DEP_MOD_GT;
} else {
mode = ALPM_DEP_MOD_ANY;
}
switch(mode) {
case ALPM_DEP_MOD_ANY:
name = actualDependency.toString();
break;
case ALPM_DEP_MOD_GE:
case ALPM_DEP_MOD_LE:
name = actualDependency.mid(0, suffixBeg).toString();
version = actualDependency.mid(suffixBeg + 2).toString();
break;
case ALPM_DEP_MOD_EQ:
case ALPM_DEP_MOD_LT:
case ALPM_DEP_MOD_GT:
name = actualDependency.mid(0, suffixBeg).toString();
version = actualDependency.mid(suffixBeg + 1).toString();
break;
default:
;
}
}
QString Dependency::toString() const
{
switch(mode) {
case ALPM_DEP_MOD_EQ:
return name % QChar('=') % version;
case ALPM_DEP_MOD_GE:
return name % QChar('>') % QChar('=') % version;
case ALPM_DEP_MOD_LE:
return name % QChar('<') % QChar('=') % version;
case ALPM_DEP_MOD_GT:
return name % QChar('>') % version;
case ALPM_DEP_MOD_LT:
return name % QChar('<') % version;
default:
return name;
}
}
/*!
* \brief Returns a JSON object for the current instance.
*/
QJsonObject Dependency::toJson() const
{
QJsonObject obj;
obj.insert(QStringLiteral("name"), name);
obj.insert(QStringLiteral("ver"), version);
switch(mode) {
case ALPM_DEP_MOD_ANY:
obj.insert(QStringLiteral("mod"), QStringLiteral("any"));
break;
case ALPM_DEP_MOD_EQ:
obj.insert(QStringLiteral("mod"), QStringLiteral("eq"));
break;
case ALPM_DEP_MOD_GE:
obj.insert(QStringLiteral("mod"), QStringLiteral("ge"));
break;
case ALPM_DEP_MOD_LE:
obj.insert(QStringLiteral("mod"), QStringLiteral("le"));
break;
case ALPM_DEP_MOD_GT:
obj.insert(QStringLiteral("mod"), QStringLiteral("gt"));
break;
case ALPM_DEP_MOD_LT:
obj.insert(QStringLiteral("mod"), QStringLiteral("lt"));
break;
default:
;
}
if(!description.isEmpty()) {
obj.insert(QStringLiteral("desc"), description);
}
return obj;
}
}