refact: change implementation of UpdateApplicationInfo
adjust the way of get XDG_DATA_DIRS. Signed-off-by: ComixHe <heyuming@deepin.org> Signed-off-by: black-desk <me@black-desk.cn>
This commit is contained in:
		@ -3,7 +3,7 @@
 | 
			
		||||
    <interface name="org.desktopspec.ApplicationManager1">
 | 
			
		||||
        <property type="ao" access="read" name="List" />
 | 
			
		||||
        <method name="UpdateApplicationInfo">
 | 
			
		||||
            <arg type="as" name="app_id" direction="in" />
 | 
			
		||||
            <arg type="as" name="app_ids" direction="in" />
 | 
			
		||||
            <annotation
 | 
			
		||||
                name="org.freedesktop.DBus.Description"
 | 
			
		||||
                value="This method is used to update the desktop file cache when needed.
 | 
			
		||||
 | 
			
		||||
@ -28,21 +28,11 @@ int main(int argc, char *argv[])
 | 
			
		||||
    registerComplexDbusType();
 | 
			
		||||
    ApplicationManager1Service AMService{std::make_unique<CGroupsIdentifier>(), AMBus};
 | 
			
		||||
    QList<DesktopFile> fileList{};
 | 
			
		||||
    QByteArray XDGDataDirs;
 | 
			
		||||
    XDGDataDirs = qgetenv("XDG_DATA_DIRS");
 | 
			
		||||
    if (XDGDataDirs.isEmpty()) {
 | 
			
		||||
        XDGDataDirs.append("/usr/local/share/:/usr/share/");
 | 
			
		||||
        qputenv("XDG_DATA_DIRS", XDGDataDirs);
 | 
			
		||||
    }
 | 
			
		||||
    auto desktopFileDirs = QString::fromLocal8Bit(XDGDataDirs).split(':', Qt::SkipEmptyParts);
 | 
			
		||||
 | 
			
		||||
    std::for_each(desktopFileDirs.begin(), desktopFileDirs.end(), [](QString &str) {
 | 
			
		||||
        str = QDir::cleanPath(str) + QDir::separator() + "applications";
 | 
			
		||||
    });
 | 
			
		||||
    auto desktopFileDirs = getXDGDataDirs();
 | 
			
		||||
 | 
			
		||||
    applyIteratively(QList<QDir>(desktopFileDirs.begin(), desktopFileDirs.end()), [&AMService](const QFileInfo &info) -> bool {
 | 
			
		||||
        ParseError err{ParseError::NoError};
 | 
			
		||||
        auto ret = DesktopFile::searchDesktopFile(info.absoluteFilePath(), err);
 | 
			
		||||
        DesktopErrorCode err{DesktopErrorCode::NoError};
 | 
			
		||||
        auto ret = DesktopFile::searchDesktopFileByPath(info.absoluteFilePath(), err);
 | 
			
		||||
        if (!ret.has_value()) {
 | 
			
		||||
            qWarning() << "failed to search File:" << err;
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
@ -132,8 +132,10 @@ QList<QDBusObjectPath> ApplicationManager1Service::list() const
 | 
			
		||||
 | 
			
		||||
void ApplicationManager1Service::removeOneApplication(const QDBusObjectPath &application)
 | 
			
		||||
{
 | 
			
		||||
    unregisterObjectFromDBus(application.path());
 | 
			
		||||
    m_applicationList.remove(application);
 | 
			
		||||
    if (auto it = m_applicationList.find(application); it != m_applicationList.cend()) {
 | 
			
		||||
        unregisterObjectFromDBus(application.path());
 | 
			
		||||
        m_applicationList.remove(application);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationManager1Service::removeAllApplication()
 | 
			
		||||
@ -221,80 +223,48 @@ QDBusObjectPath ApplicationManager1Service::Launch(const QString &id,
 | 
			
		||||
    return value->Launch(actions, fields, options);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationManager1Service::UpdateApplicationInfo(const QStringList &app_id)
 | 
			
		||||
void ApplicationManager1Service::updateApplication(const QSharedPointer<ApplicationService> &destApp,
 | 
			
		||||
                                                   const DesktopFile &desktopFile) noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto XDGDataDirs = QString::fromLocal8Bit(qgetenv("XDG_DATA_DIRS")).split(':', Qt::SkipEmptyParts);
 | 
			
		||||
    std::for_each(XDGDataDirs.begin(), XDGDataDirs.end(), [](QString &str) {
 | 
			
		||||
        if (!str.endsWith(QDir::separator())) {
 | 
			
		||||
            str.append(QDir::separator());
 | 
			
		||||
        }
 | 
			
		||||
        str.append("applications");
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    for (auto id : app_id) {
 | 
			
		||||
        auto destApp = std::find_if(m_applicationList.begin(),
 | 
			
		||||
                                    m_applicationList.end(),
 | 
			
		||||
                                    [&id](const QSharedPointer<ApplicationService> &value) { return value->id() == id; });
 | 
			
		||||
 | 
			
		||||
        if (destApp == m_applicationList.end()) {  // new app
 | 
			
		||||
            qInfo() << "add a new application:" << id;
 | 
			
		||||
            do {
 | 
			
		||||
                for (const auto &suffix : XDGDataDirs) {
 | 
			
		||||
                    QFileInfo info{suffix + id};
 | 
			
		||||
                    if (info.exists()) {
 | 
			
		||||
                        ParseError err;
 | 
			
		||||
                        auto file = DesktopFile::searchDesktopFile(info.absoluteFilePath(), err);
 | 
			
		||||
 | 
			
		||||
                        if (!file.has_value()) {
 | 
			
		||||
                            continue;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        if (!addApplication(std::move(file).value())) {
 | 
			
		||||
                            id.clear();
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (id.isEmpty()) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                auto hyphenIndex = id.indexOf('-');
 | 
			
		||||
                if (hyphenIndex == -1) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                id[hyphenIndex] = QDir::separator();
 | 
			
		||||
            } while (true);
 | 
			
		||||
        } else {  // remove or update
 | 
			
		||||
            if (!(*destApp)->m_isPersistence) [[unlikely]] {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            auto filePath = (*destApp)->m_desktopSource.m_file.filePath();
 | 
			
		||||
            if (QFileInfo::exists(filePath)) {  // update
 | 
			
		||||
                qInfo() << "update application:" << id;
 | 
			
		||||
                struct stat buf;
 | 
			
		||||
                if (auto ret = stat(filePath.toLatin1().data(), &buf); ret == -1) {
 | 
			
		||||
                    qWarning() << "get file" << filePath << "state failed:" << std::strerror(errno) << ", skip...";
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if ((*destApp)->m_desktopSource.m_file.modified(
 | 
			
		||||
                        static_cast<std::size_t>(buf.st_mtim.tv_sec * 1e9 + buf.st_mtim.tv_nsec))) {
 | 
			
		||||
                    auto newEntry = new DesktopEntry{};
 | 
			
		||||
                    auto err = newEntry->parse((*destApp)->m_desktopSource.m_file);
 | 
			
		||||
                    if (err != ParseError::NoError and err != ParseError::EntryKeyInvalid) {
 | 
			
		||||
                        qWarning() << "update desktop file failed:" << err << ", content wouldn't change.";
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                    (*destApp)->m_entry.reset(newEntry);
 | 
			
		||||
                }
 | 
			
		||||
            } else {  // remove
 | 
			
		||||
                qInfo() << "remove application:" << id;
 | 
			
		||||
                removeOneApplication((*destApp)->m_applicationPath);
 | 
			
		||||
            }
 | 
			
		||||
    struct stat buf;
 | 
			
		||||
    const auto *filePath = desktopFile.filePath().toLocal8Bit().data();
 | 
			
		||||
    if (auto ret = stat(filePath, &buf); ret == -1) {
 | 
			
		||||
        qWarning() << "get file" << filePath << "state failed:" << std::strerror(errno);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constexpr std::size_t secToNano = 1e9;
 | 
			
		||||
 | 
			
		||||
    if (destApp->m_desktopSource.m_file.modified(buf.st_mtim.tv_sec * secToNano + buf.st_mtim.tv_nsec)) {
 | 
			
		||||
        auto newEntry = new DesktopEntry{};
 | 
			
		||||
        auto err = newEntry->parse(destApp->m_desktopSource.m_file);
 | 
			
		||||
        if (err != DesktopErrorCode::NoError and err != DesktopErrorCode::EntryKeyInvalid) {
 | 
			
		||||
            qWarning() << "update desktop file failed:" << err << ", content wouldn't change.";
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        destApp->m_entry.reset(newEntry);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationManager1Service::UpdateApplicationInfo(const QStringList &appIdList)
 | 
			
		||||
{
 | 
			
		||||
    for (const auto &appId : appIdList) {
 | 
			
		||||
        DesktopErrorCode err{DesktopErrorCode::NotFound};
 | 
			
		||||
        auto file = DesktopFile::searchDesktopFileById(appId, err);
 | 
			
		||||
        auto destApp = std::find_if(m_applicationList.cbegin(),
 | 
			
		||||
                                    m_applicationList.cend(),
 | 
			
		||||
                                    [&appId](const QSharedPointer<ApplicationService> &app) { return appId == app->id(); });
 | 
			
		||||
 | 
			
		||||
        if (err == DesktopErrorCode::NotFound) {
 | 
			
		||||
            removeOneApplication(destApp.key());
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (destApp != m_applicationList.cend()) {
 | 
			
		||||
            updateApplication(destApp.value(), file.value());
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        addApplication(std::move(file).value());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -48,13 +48,15 @@ public:
 | 
			
		||||
    void removeOneApplication(const QDBusObjectPath &application);
 | 
			
		||||
    void removeAllApplication();
 | 
			
		||||
 | 
			
		||||
    void updateApplication(const QSharedPointer<ApplicationService> &deskApp, const DesktopFile &desktopFile) noexcept;
 | 
			
		||||
 | 
			
		||||
    JobManager1Service &jobManager() noexcept { return *m_jobManager; }
 | 
			
		||||
 | 
			
		||||
public Q_SLOTS:
 | 
			
		||||
    [[nodiscard]] QDBusObjectPath Application(const QString &id) const;
 | 
			
		||||
    QString Identify(const QDBusUnixFileDescriptor &pidfd, QDBusObjectPath &application, QDBusObjectPath &application_instance);
 | 
			
		||||
    QDBusObjectPath Launch(const QString &id, const QString &action, const QStringList &fields, const QVariantMap &options);
 | 
			
		||||
    void UpdateApplicationInfo(const QStringList &app_id);
 | 
			
		||||
    void UpdateApplicationInfo(const QStringList &appIdList);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::unique_ptr<Identifier> m_identifier;
 | 
			
		||||
 | 
			
		||||
@ -30,7 +30,7 @@ public:
 | 
			
		||||
    ApplicationService &operator=(const ApplicationService &) = delete;
 | 
			
		||||
    ApplicationService &operator=(ApplicationService &&) = delete;
 | 
			
		||||
 | 
			
		||||
    Q_PROPERTY(QStringList Actions READ actions CONSTANT)
 | 
			
		||||
    Q_PROPERTY(QStringList Actions READ actions)
 | 
			
		||||
    [[nodiscard]] QStringList actions() const noexcept;
 | 
			
		||||
 | 
			
		||||
    Q_PROPERTY(QString ID READ id CONSTANT)
 | 
			
		||||
@ -89,8 +89,8 @@ private:
 | 
			
		||||
            sourceStream.setString(&m_desktopSource.m_temp, QTextStream::ReadOnly | QTextStream::Text);
 | 
			
		||||
        }
 | 
			
		||||
        m_entry.reset(new DesktopEntry());
 | 
			
		||||
        if (auto error = m_entry->parse(sourceStream); error != ParseError::NoError) {
 | 
			
		||||
            if (error != ParseError::EntryKeyInvalid) {
 | 
			
		||||
        if (auto error = m_entry->parse(sourceStream); error != DesktopErrorCode::NoError) {
 | 
			
		||||
            if (error != DesktopErrorCode::EntryKeyInvalid) {
 | 
			
		||||
                m_entry.reset(nullptr);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -23,10 +23,10 @@ auto DesktopEntry::parserGroupHeader(const QString &str) noexcept
 | 
			
		||||
    return it;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ParseError DesktopEntry::parseEntry(const QString &str, decltype(m_entryMap)::iterator ¤tGroup) noexcept
 | 
			
		||||
DesktopErrorCode DesktopEntry::parseEntry(const QString &str, decltype(m_entryMap)::iterator ¤tGroup) noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (str.startsWith("#")) {
 | 
			
		||||
        return ParseError::NoError;
 | 
			
		||||
        return DesktopErrorCode::NoError;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto splitCharIndex = str.indexOf(']');
 | 
			
		||||
@ -57,7 +57,7 @@ ParseError DesktopEntry::parseEntry(const QString &str, decltype(m_entryMap)::it
 | 
			
		||||
    auto matcher = re.match(keyStr);
 | 
			
		||||
    if (!matcher.hasMatch()) {
 | 
			
		||||
        qWarning() << "invalid key: " << keyStr;
 | 
			
		||||
        return ParseError::EntryKeyInvalid;
 | 
			
		||||
        return DesktopErrorCode::EntryKeyInvalid;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    key = matcher.captured("MainKey");
 | 
			
		||||
@ -69,110 +69,122 @@ ParseError DesktopEntry::parseEntry(const QString &str, decltype(m_entryMap)::it
 | 
			
		||||
    auto cur = currentGroup->find(key);
 | 
			
		||||
    if (cur == currentGroup->end()) {
 | 
			
		||||
        currentGroup->insert(keyStr, {{valueKey, valueStr}});
 | 
			
		||||
        return ParseError::NoError;
 | 
			
		||||
        return DesktopErrorCode::NoError;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto value = cur->find(valueKey);
 | 
			
		||||
    if (value == cur->end()) {
 | 
			
		||||
        cur->insert(valueKey, valueStr);
 | 
			
		||||
        return ParseError::NoError;
 | 
			
		||||
        return DesktopErrorCode::NoError;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qWarning() << "duplicated postfix and this line will be aborted, maybe format is invalid.\n"
 | 
			
		||||
               << "exist: " << value.key() << "[" << value.value() << "]"
 | 
			
		||||
               << "current: " << str;
 | 
			
		||||
 | 
			
		||||
    return ParseError::NoError;
 | 
			
		||||
    return DesktopErrorCode::NoError;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<DesktopFile> DesktopFile::searchDesktopFile(const QString &desktopFile, ParseError &err) noexcept
 | 
			
		||||
std::optional<DesktopFile> DesktopFile::searchDesktopFileByPath(const QString &desktopFile, DesktopErrorCode &err) noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (auto tmp = desktopFile.split("."); tmp.last() != "desktop") {
 | 
			
		||||
        qWarning() << "file isn't a desktop file";
 | 
			
		||||
        err = ParseError::MismatchedFile;
 | 
			
		||||
    constexpr decltype(auto) desktopPostfix = ".desktop";
 | 
			
		||||
 | 
			
		||||
    if (!desktopFile.endsWith(desktopPostfix)) {
 | 
			
		||||
        qWarning() << "file isn't a desktop file:" << desktopFile;
 | 
			
		||||
        err = DesktopErrorCode::MismatchedFile;
 | 
			
		||||
        return std::nullopt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QString path;
 | 
			
		||||
    QFileInfo fileinfo{desktopFile};
 | 
			
		||||
    if (!fileinfo.isAbsolute() or !fileinfo.exists()) {
 | 
			
		||||
        qWarning() << "desktop file not found.";
 | 
			
		||||
        err = DesktopErrorCode::NotFound;
 | 
			
		||||
        return std::nullopt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QString path{desktopFile};
 | 
			
		||||
    QString id;
 | 
			
		||||
 | 
			
		||||
    QFileInfo Fileinfo{desktopFile};
 | 
			
		||||
    if (Fileinfo.isAbsolute() and Fileinfo.exists()) {
 | 
			
		||||
        path = desktopFile;
 | 
			
		||||
    } else {
 | 
			
		||||
        auto XDGDataDirs = QString::fromLocal8Bit(qgetenv("XDG_DATA_DIRS")).split(':', Qt::SkipEmptyParts);
 | 
			
		||||
        std::for_each(XDGDataDirs.begin(), XDGDataDirs.end(), [](QString &str) {
 | 
			
		||||
            str = QDir::cleanPath(str) + QDir::separator() + "applications";
 | 
			
		||||
        });
 | 
			
		||||
        auto fileName = Fileinfo.fileName();
 | 
			
		||||
    const auto &XDGDataDirs = getXDGDataDirs();
 | 
			
		||||
    auto idGen = std::any_of(XDGDataDirs.cbegin(), XDGDataDirs.cend(), [&desktopFile](const QString &suffixPath) {
 | 
			
		||||
        return desktopFile.startsWith(suffixPath);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
        applyIteratively(QList<QDir>{XDGDataDirs.begin(), XDGDataDirs.end()}, [&fileName, &path](const QFileInfo &file) -> bool {
 | 
			
		||||
            if (file.fileName() == fileName) {
 | 
			
		||||
                path = file.absoluteFilePath();
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            return false;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (path.isEmpty()) {
 | 
			
		||||
        qWarning() << "desktop file not found.";
 | 
			
		||||
        err = ParseError::NotFound;
 | 
			
		||||
        return std::nullopt;
 | 
			
		||||
    }
 | 
			
		||||
    auto tmp = path.chopped(8);  // remove ".desktop"
 | 
			
		||||
    auto components = tmp.split(QDir::separator()).toList();
 | 
			
		||||
    auto it = std::find(components.cbegin(), components.cend(), "applications");
 | 
			
		||||
    if (it == components.cend()) {
 | 
			
		||||
        qWarning() << "custom location detected, Id wouldn't be generated.";
 | 
			
		||||
    } else {
 | 
			
		||||
    if (idGen) {
 | 
			
		||||
        auto tmp = path.chopped(sizeof(desktopPostfix) - 1);
 | 
			
		||||
        auto components = tmp.split(QDir::separator()).toList();
 | 
			
		||||
        auto it = std::find(components.cbegin(), components.cend(), "applications");
 | 
			
		||||
        QString FileId;
 | 
			
		||||
        ++it;
 | 
			
		||||
        while (it != components.cend())
 | 
			
		||||
        while (it != components.cend()) {
 | 
			
		||||
            FileId += (*(it++) + "-");
 | 
			
		||||
        }
 | 
			
		||||
        id = FileId.chopped(1);  // remove extra "-""
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct stat buf;
 | 
			
		||||
    if (auto ret = stat(path.toLatin1().data(), &buf); ret == -1) {
 | 
			
		||||
        err = ParseError::OpenFailed;
 | 
			
		||||
        err = DesktopErrorCode::OpenFailed;
 | 
			
		||||
        qWarning() << "get file" << path << "state failed:" << std::strerror(errno);
 | 
			
		||||
        return std::nullopt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    err = ParseError::NoError;
 | 
			
		||||
    err = DesktopErrorCode::NoError;
 | 
			
		||||
    constexpr std::size_t nanoToSec = 1e9;
 | 
			
		||||
 | 
			
		||||
    return DesktopFile{std::move(path), std::move(id), buf.st_mtim.tv_sec * nanoToSec + buf.st_mtim.tv_nsec};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<DesktopFile> DesktopFile::searchDesktopFileById(const QString &appId, DesktopErrorCode &err) noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto XDGDataDirs = getXDGDataDirs();
 | 
			
		||||
 | 
			
		||||
    for (const auto &dir : XDGDataDirs) {
 | 
			
		||||
        auto app = QFileInfo{dir + QDir::separator() + appId};
 | 
			
		||||
        while (!app.exists()) {
 | 
			
		||||
            auto filePath = app.absoluteFilePath();
 | 
			
		||||
            auto hyphenIndex = filePath.indexOf('-');
 | 
			
		||||
            if (hyphenIndex == -1) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            filePath.replace(hyphenIndex, 1, QDir::separator());
 | 
			
		||||
            app.setFile(filePath);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (app.exists()) {
 | 
			
		||||
            return searchDesktopFileByPath(app.absoluteFilePath(), err);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return std::nullopt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool DesktopFile::modified(std::size_t time) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return time != m_mtime;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ParseError DesktopEntry::parse(const DesktopFile &desktopFile) noexcept
 | 
			
		||||
DesktopErrorCode DesktopEntry::parse(const DesktopFile &appId) noexcept
 | 
			
		||||
{
 | 
			
		||||
    auto file = QFile(desktopFile.filePath());
 | 
			
		||||
    auto file = QFile(appId.filePath());
 | 
			
		||||
    if (!file.open(QFile::ExistingOnly | QFile::ReadOnly | QFile::Text)) {
 | 
			
		||||
        qWarning() << desktopFile.filePath() << "can't open.";
 | 
			
		||||
        return ParseError::OpenFailed;
 | 
			
		||||
        qWarning() << appId.filePath() << "can't open.";
 | 
			
		||||
        return DesktopErrorCode::OpenFailed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QTextStream in{&file};
 | 
			
		||||
    return parse(in);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ParseError DesktopEntry::parse(QTextStream &stream) noexcept
 | 
			
		||||
DesktopErrorCode DesktopEntry::parse(QTextStream &stream) noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (stream.atEnd()) {
 | 
			
		||||
        return ParseError::OpenFailed;
 | 
			
		||||
        return DesktopErrorCode::OpenFailed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    stream.setEncoding(QStringConverter::Utf8);
 | 
			
		||||
    decltype(m_entryMap)::iterator currentGroup;
 | 
			
		||||
 | 
			
		||||
    ParseError err{ParseError::NoError};
 | 
			
		||||
    DesktopErrorCode err{DesktopErrorCode::NoError};
 | 
			
		||||
    while (!stream.atEnd()) {
 | 
			
		||||
        auto line = stream.readLine().trimmed();
 | 
			
		||||
 | 
			
		||||
@ -182,13 +194,13 @@ ParseError DesktopEntry::parse(QTextStream &stream) noexcept
 | 
			
		||||
 | 
			
		||||
        if (line.startsWith("[")) {
 | 
			
		||||
            if (!line.endsWith("]")) {
 | 
			
		||||
                return ParseError::GroupHeaderInvalid;
 | 
			
		||||
                return DesktopErrorCode::GroupHeaderInvalid;
 | 
			
		||||
            }
 | 
			
		||||
            currentGroup = parserGroupHeader(line);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (auto error = parseEntry(line, currentGroup); error != ParseError::NoError) {
 | 
			
		||||
        if (auto error = parseEntry(line, currentGroup); error != DesktopErrorCode::NoError) {
 | 
			
		||||
            err = error;
 | 
			
		||||
            qWarning() << "an error occurred,this line will be skipped:" << line;
 | 
			
		||||
        }
 | 
			
		||||
@ -329,36 +341,36 @@ QDebug operator<<(QDebug debug, const DesktopEntry::Value &v)
 | 
			
		||||
    return debug;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QDebug operator<<(QDebug debug, const ParseError &v)
 | 
			
		||||
QDebug operator<<(QDebug debug, const DesktopErrorCode &v)
 | 
			
		||||
{
 | 
			
		||||
    QDebugStateSaver saver{debug};
 | 
			
		||||
    QString errMsg;
 | 
			
		||||
    switch (v) {
 | 
			
		||||
        case ParseError::NoError: {
 | 
			
		||||
        case DesktopErrorCode::NoError: {
 | 
			
		||||
            errMsg = "no error.";
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case ParseError::NotFound: {
 | 
			
		||||
        case DesktopErrorCode::NotFound: {
 | 
			
		||||
            errMsg = "file not found.";
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case ParseError::MismatchedFile: {
 | 
			
		||||
        case DesktopErrorCode::MismatchedFile: {
 | 
			
		||||
            errMsg = "file type is mismatched.";
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case ParseError::InvalidLocation: {
 | 
			
		||||
        case DesktopErrorCode::InvalidLocation: {
 | 
			
		||||
            errMsg = "file location is invalid, please check $XDG_DATA_DIRS.";
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case ParseError::OpenFailed: {
 | 
			
		||||
        case DesktopErrorCode::OpenFailed: {
 | 
			
		||||
            errMsg = "couldn't open the file.";
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case ParseError::GroupHeaderInvalid: {
 | 
			
		||||
        case DesktopErrorCode::GroupHeaderInvalid: {
 | 
			
		||||
            errMsg = "groupHead syntax is invalid.";
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case ParseError::EntryKeyInvalid: {
 | 
			
		||||
        case DesktopErrorCode::EntryKeyInvalid: {
 | 
			
		||||
            errMsg = "key syntax is invalid.";
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,15 @@
 | 
			
		||||
 | 
			
		||||
constexpr static auto defaultKeyStr = "default";
 | 
			
		||||
 | 
			
		||||
enum class ParseError { NoError, NotFound, MismatchedFile, InvalidLocation, OpenFailed, GroupHeaderInvalid, EntryKeyInvalid };
 | 
			
		||||
enum class DesktopErrorCode {
 | 
			
		||||
    NoError,
 | 
			
		||||
    NotFound,
 | 
			
		||||
    MismatchedFile,
 | 
			
		||||
    InvalidLocation,
 | 
			
		||||
    OpenFailed,
 | 
			
		||||
    GroupHeaderInvalid,
 | 
			
		||||
    EntryKeyInvalid
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct DesktopFile
 | 
			
		||||
{
 | 
			
		||||
@ -25,7 +33,8 @@ struct DesktopFile
 | 
			
		||||
    [[nodiscard]] const QString &filePath() const { return m_filePath; }
 | 
			
		||||
    [[nodiscard]] const QString &desktopId() const { return m_desktopId; }
 | 
			
		||||
 | 
			
		||||
    static std::optional<DesktopFile> searchDesktopFile(const QString &desktopFilePath, ParseError &err) noexcept;
 | 
			
		||||
    static std::optional<DesktopFile> searchDesktopFileById(const QString &appId, DesktopErrorCode &err) noexcept;
 | 
			
		||||
    static std::optional<DesktopFile> searchDesktopFileByPath(const QString &desktopFilePath, DesktopErrorCode &err) noexcept;
 | 
			
		||||
    [[nodiscard]] bool modified(std::size_t time) const noexcept;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
@ -66,17 +75,17 @@ public:
 | 
			
		||||
    DesktopEntry &operator=(DesktopEntry &&) = default;
 | 
			
		||||
 | 
			
		||||
    ~DesktopEntry() = default;
 | 
			
		||||
    [[nodiscard]] ParseError parse(const DesktopFile &file) noexcept;
 | 
			
		||||
    [[nodiscard]] ParseError parse(QTextStream &stream) noexcept;
 | 
			
		||||
    [[nodiscard]] DesktopErrorCode parse(const DesktopFile &file) noexcept;
 | 
			
		||||
    [[nodiscard]] DesktopErrorCode parse(QTextStream &stream) noexcept;
 | 
			
		||||
    [[nodiscard]] std::optional<QMap<QString, Value>> group(const QString &key) const noexcept;
 | 
			
		||||
    [[nodiscard]] std::optional<Value> value(const QString &key, const QString &valueKey) const noexcept;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    QMap<QString, QMap<QString, Value>> m_entryMap;
 | 
			
		||||
    auto parserGroupHeader(const QString &str) noexcept;
 | 
			
		||||
    static ParseError parseEntry(const QString &str, decltype(m_entryMap)::iterator ¤tGroup) noexcept;
 | 
			
		||||
    static DesktopErrorCode parseEntry(const QString &str, decltype(m_entryMap)::iterator ¤tGroup) noexcept;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
QDebug operator<<(QDebug debug, const DesktopEntry::Value &v);
 | 
			
		||||
 | 
			
		||||
QDebug operator<<(QDebug debug, const ParseError &v);
 | 
			
		||||
QDebug operator<<(QDebug debug, const DesktopErrorCode &v);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										31
									
								
								src/global.h
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								src/global.h
									
									
									
									
									
								
							@ -25,13 +25,13 @@ using IconMap = QMap<QString, QMap<uint, QMap<QString, QDBusUnixFileDescriptor>>
 | 
			
		||||
 | 
			
		||||
inline QString getApplicationLauncherBinary()
 | 
			
		||||
{
 | 
			
		||||
    static const QString bin = []() -> QString {
 | 
			
		||||
    static const QString bin = []() {
 | 
			
		||||
        auto value = qgetenv("DEEPIN_APPLICATION_MANAGER_APP_LAUNCH_HELPER_BIN");
 | 
			
		||||
        if (value.isEmpty()) {
 | 
			
		||||
            return ApplicationLaunchHelperBinary;
 | 
			
		||||
            return QString::fromLocal8Bit(ApplicationLaunchHelperBinary);
 | 
			
		||||
        }
 | 
			
		||||
        qWarning() << "Using app launch helper defined in environment variable DEEPIN_APPLICATION_MANAGER_APP_LAUNCH_HELPER_BIN.";
 | 
			
		||||
        return value;
 | 
			
		||||
        return QString::fromLocal8Bit(value);
 | 
			
		||||
    }();
 | 
			
		||||
    return bin;
 | 
			
		||||
}
 | 
			
		||||
@ -303,4 +303,29 @@ inline QString getRelativePathFromAppId(const QString &id)
 | 
			
		||||
    return path;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline QStringList getXDGDataDirs()
 | 
			
		||||
{
 | 
			
		||||
    const static auto XDGDataDirs = []() {
 | 
			
		||||
        auto env = QString::fromLocal8Bit(qgetenv("XDG_DATA_DIRS")).split(':', Qt::SkipEmptyParts);
 | 
			
		||||
 | 
			
		||||
        if (env.isEmpty()) {
 | 
			
		||||
            env.append(QString{qgetenv("HOME")} + QDir::separator() + ".local/share");
 | 
			
		||||
            env.append("/usr/local/share");
 | 
			
		||||
            env.append("/usr/share");
 | 
			
		||||
            qputenv("XDG_DATA_DIRS", env.join(':').toLocal8Bit());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::for_each(env.begin(), env.end(), [](QString &str) {
 | 
			
		||||
            if (!str.endsWith(QDir::separator())) {
 | 
			
		||||
                str.append(QDir::separator());
 | 
			
		||||
            }
 | 
			
		||||
            str.append("applications");
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return env;
 | 
			
		||||
    }();
 | 
			
		||||
 | 
			
		||||
    return XDGDataDirs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@ -33,18 +33,20 @@ bool SystemdSignalDispatcher::connectToSignals() noexcept
 | 
			
		||||
 | 
			
		||||
void SystemdSignalDispatcher::onUnitNew(QString unitName, QDBusObjectPath systemdUnitPath)
 | 
			
		||||
{
 | 
			
		||||
    if (!unitName.startsWith("app-")) {
 | 
			
		||||
    constexpr decltype(auto) appPrefix = u8"app-";
 | 
			
		||||
    if (!unitName.startsWith(appPrefix)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    emit SystemdUnitNew(unitName.sliced(sizeof("app-") - 1), systemdUnitPath);
 | 
			
		||||
    emit SystemdUnitNew(unitName.sliced(sizeof(appPrefix) - 1), systemdUnitPath);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SystemdSignalDispatcher::onUnitRemoved(QString unitName, QDBusObjectPath systemdUnitPath)
 | 
			
		||||
{
 | 
			
		||||
    if (!unitName.startsWith("app-")) {
 | 
			
		||||
    constexpr decltype(auto) appPrefix = u8"app-";
 | 
			
		||||
    if (!unitName.startsWith(appPrefix)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    emit SystemdUnitRemoved(unitName.sliced(sizeof("app-") - 1), systemdUnitPath);
 | 
			
		||||
    emit SystemdUnitRemoved(unitName.sliced(sizeof(appPrefix) - 1), systemdUnitPath);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -17,8 +17,8 @@ public:
 | 
			
		||||
    {
 | 
			
		||||
        auto curDir = QDir::current();
 | 
			
		||||
        QString path{curDir.absolutePath() + "/data/desktopExample.desktop"};
 | 
			
		||||
        ParseError err;
 | 
			
		||||
        auto file = DesktopFile::searchDesktopFile(path, err);
 | 
			
		||||
        DesktopErrorCode err;
 | 
			
		||||
        auto file = DesktopFile::searchDesktopFileByPath(path, err);
 | 
			
		||||
        if (!file.has_value()) {
 | 
			
		||||
            qWarning() << "search " << path << "failed:" << err;
 | 
			
		||||
            return;
 | 
			
		||||
@ -54,7 +54,7 @@ TEST_F(TestDesktopEntry, prase)
 | 
			
		||||
    ASSERT_TRUE(in.open(QFile::ExistingOnly | QFile::ReadOnly | QFile::Text));
 | 
			
		||||
    QTextStream fs{&in};
 | 
			
		||||
    auto err = entry.parse(fs);
 | 
			
		||||
    ASSERT_EQ(err, ParseError::NoError);
 | 
			
		||||
    ASSERT_EQ(err, DesktopErrorCode::NoError);
 | 
			
		||||
 | 
			
		||||
    auto group = entry.group("Desktop Entry");
 | 
			
		||||
    ASSERT_TRUE(group);
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user