From ca34562a1c59fe8869a14e43ffe86a5b4df7475e Mon Sep 17 00:00:00 2001 From: Vyn Date: Thu, 9 May 2024 14:39:10 +0200 Subject: [PATCH] Change 'File' concept to a 'Resource' abstract concept --- CMakeLists.txt | 9 +++- src/Backend.cpp | 79 ++++++++++++++++----------- src/Backend.h | 17 +++--- src/TasksFile.cpp | 7 ++- src/TasksFile.h | 5 +- src/core/BaseFileResource.h | 42 +++++++++++++++ src/core/BaseResource.cpp | 52 ++++++++++++++++++ src/core/BaseResource.h | 54 +++++++++++++++++++ src/core/Mirai.cpp | 68 +++++++++-------------- src/core/Mirai.h | 15 +++--- src/core/StdFileResource.h | 63 ++++++++++++++++++++++ src/core/TaskItem.cpp | 4 +- src/core/TaskItem.h | 8 +-- src/core/TasksFile.cpp | 99 ---------------------------------- src/core/TasksFile.h | 60 --------------------- src/core/TasksView.cpp | 17 +++--- src/core/TasksView.h | 8 ++- src/core/TodoMd.cpp | 64 ---------------------- src/core/TodoMd.h | 65 ++++++++++++++++++++-- src/qml/SideMenu.qml | 8 +-- src/qml/forms/FilesForm.qml | 4 +- tests/{test.cpp => format.cpp} | 2 - tests/saving.cpp | 48 +++++++++++++++++ 23 files changed, 447 insertions(+), 351 deletions(-) create mode 100644 src/core/BaseFileResource.h create mode 100644 src/core/BaseResource.cpp create mode 100644 src/core/BaseResource.h create mode 100644 src/core/StdFileResource.h delete mode 100644 src/core/TasksFile.cpp delete mode 100644 src/core/TasksFile.h rename tests/{test.cpp => format.cpp} (97%) create mode 100644 tests/saving.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c6336b6..d718e6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,9 @@ qt_standard_project_setup(REQUIRES 6.7) add_library(mirai-core src/core/Mirai.h src/core/Mirai.cpp src/core/TaskItem.h src/core/TaskItem.cpp - src/core/TasksFile.h src/core/TasksFile.cpp + src/core/BaseResource.h src/core/BaseResource.cpp + src/core/BaseFileResource.h + src/core/StdFileResource.h src/core/TasksView.h src/core/TasksView.cpp src/core/TodoMd.h src/core/TodoMd.cpp src/core/utils.h src/core/utils.cpp @@ -76,6 +78,9 @@ target_link_libraries(mirai PRIVATE mirai-core) # Tests add_subdirectory(libs/Catch2) -add_executable(tests tests/test.cpp) +add_executable(tests + tests/format.cpp + tests/saving.cpp +) target_link_libraries(tests PRIVATE mirai-core) target_link_libraries(tests PRIVATE Catch2::Catch2WithMain) diff --git a/src/Backend.cpp b/src/Backend.cpp index a7fa6b1..a10bb2e 100644 --- a/src/Backend.cpp +++ b/src/Backend.cpp @@ -6,12 +6,15 @@ #include "Backend.h" #include "TaskItem.h" +#include "core/StdFileResource.h" #include "core/TaskItem.h" #include "core/TasksView.h" #include "core/TodoMd.h" #include "cpp-utils/debug.h" +#include #include #include +#include #include #include #include @@ -46,7 +49,11 @@ Backend::Backend() : todoView(&mirai) } for (const QJsonValueRef &filePath : jsonFilesPath.toArray()) { cpputils::debug::Timer loadingFileDuration; - mirai.loadFile(filePath.toString().toStdString()); + auto fileResource = + std::make_unique(mirai::BaseFileResourceConstructor{ + .name = "[Can't load path]", .path = filePath.toString().toStdString() + }); + mirai.loadResource(std::move(fileResource)); loadingFileDuration.printTimeElapsed( "Loading file duration of " + filePath.toString().toStdString() ); @@ -72,23 +79,23 @@ Backend::Backend() : todoView(&mirai) startDuration.printTimeElapsed("Start duration"); } -void Backend::addTodo(QString newTodo, QString date) -{ - mirai.addTask(newTodo.toStdString(), date.toStdString()); - mirai.save(); - todoView.update(); - rebuildQMLTasksList(); - emit tasksChanged(); -} - void Backend::addTodoFromRawFormat(QString filePath, QString text, QString date) { - auto file = mirai.getFileByPath(filePath.toStdString()); - if (!file.has_value()) { + auto resource = std::ranges::find_if( + mirai.getResources(), + [&](std::unique_ptr &resource) { + auto fsResource = dynamic_cast(resource.get()); + if (!fsResource) { + return false; + } + return fsResource->getPath() == filePath.toStdString(); + } + ); + if (resource == mirai.getResources().end()) { qWarning() << "File path '" << filePath << "' doesn't exist" << Qt::endl; return; } - file.value().get().addTask( + resource->get()->addTask( mirai::TodoMdFormat::stringToTask(text.toStdString(), date.toStdString()) ); mirai.save(); @@ -111,16 +118,16 @@ void Backend::removeTagFilter(QString tag) emit tasksChanged(); } -void Backend::addFileFilter(QString fileName) +void Backend::addResourceFilter(QString fileName) { - todoView.addFileFilter(fileName.toStdString()); + todoView.addResourceFilter(fileName.toStdString()); rebuildQMLTasksList(); emit tasksChanged(); } -void Backend::removeFileFilter(QString fileName) +void Backend::removeResourceFilter(QString fileName) { - todoView.removeFileFilter(fileName.toStdString()); + todoView.removeResourceFilter(fileName.toStdString()); rebuildQMLTasksList(); emit tasksChanged(); } @@ -180,9 +187,9 @@ void Backend::hideCompletedTasks(bool shouldHide) void Backend::rebuildQMLTasksList() { // TODO MOVE TO ANOTHER FILE - QMLTasksFiles.clear(); - for (auto &file : mirai.getFiles()) { - QMLTasksFiles.push_back({.tasksFile = file.get()}); + QMLResources.clear(); + for (auto &resource : mirai.getResources()) { + QMLResources.push_back({.tasksFile = resource.get()}); } // ---- QMLTasks.clear(); @@ -224,9 +231,9 @@ void Backend::rebuildQMLTasksList() QMLActiveTagsFilter.push_back(QString::fromStdString(activeTagFilter)); } - QMLActiveFilesFilter.clear(); + QMLActiveResourcesFilter.clear(); for (auto &activeFileFilter : todoView.getActiveFilesFilter()) { - QMLActiveFilesFilter.push_back(QString::fromStdString(activeFileFilter)); + QMLActiveResourcesFilter.push_back(QString::fromStdString(activeFileFilter)); } } @@ -245,9 +252,9 @@ QVariant Backend::getActiveTagsFilter() return QVariant::fromValue(QMLActiveTagsFilter); } -QVariant Backend::getActiveFilesFilter() +QVariant Backend::getActiveResourcesFilter() { - return QVariant::fromValue(QMLActiveFilesFilter); + return QVariant::fromValue(QMLActiveResourcesFilter); } bool Backend::shouldHideCompletedTasks() @@ -255,9 +262,9 @@ bool Backend::shouldHideCompletedTasks() return shouldHideCompletedTasks_; } -QVariant Backend::getFiles() +QVariant Backend::getResources() { - return QVariant::fromValue(QMLTasksFiles); + return QVariant::fromValue(QMLResources); } QString Backend::getTagColor(QString tag) @@ -289,13 +296,20 @@ void Backend::saveFilesPath(QVariant modifiedFilesPath) auto paths = modifiedFilesPath.toStringList(); QMLTags.clear(); QMLTasks.clear(); - QMLTasksFiles.clear(); + QMLResources.clear(); emit tagsChanged(); emit tasksChanged(); emit filesChanged(); - mirai.unloadAllFiles(); + mirai.unloadAllResources(); for (auto path : paths) { - mirai.loadFile(path.toStdString()); + if (path == "") { + continue; + } + auto fileResource = + std::make_unique(mirai::BaseFileResourceConstructor{ + .name = "[Can't load path]", .path = path.toStdString() + }); + mirai.loadResource(std::move(fileResource)); } todoView.update(); rebuildQMLTasksList(); @@ -310,8 +324,11 @@ void Backend::saveConfig() QJsonObject rootJson; QJsonArray filesJson; - for (auto &file : mirai.getFiles()) { - filesJson.append(QString::fromStdString(file->getPath())); + for (auto &file : mirai.getResources()) { + auto fsResource = dynamic_cast(file.get()); + if (fsResource) { + filesJson.append(QString::fromStdString(fsResource->getPath())); + } } rootJson["files"] = filesJson; diff --git a/src/Backend.h b/src/Backend.h index e6d0a0f..05d9631 100644 --- a/src/Backend.h +++ b/src/Backend.h @@ -33,22 +33,21 @@ class Backend : public QObject Q_OBJECT QML_ELEMENT Q_PROPERTY(QVariant tasks READ getTasks NOTIFY tasksChanged) - Q_PROPERTY(QVariant files READ getFiles NOTIFY filesChanged) + Q_PROPERTY(QVariant files READ getResources NOTIFY filesChanged) Q_PROPERTY(QVariant tags READ getTags NOTIFY tagsChanged) Q_PROPERTY(QVariant activeTagsFilter READ getActiveTagsFilter NOTIFY tasksChanged) - Q_PROPERTY(QVariant activeFilesFilter READ getActiveFilesFilter NOTIFY tasksChanged) + Q_PROPERTY(QVariant activeResourcesFilter READ getActiveResourcesFilter NOTIFY tasksChanged) Q_PROPERTY(bool shouldHideCompletedTasks READ shouldHideCompletedTasks WRITE hideCompletedTasks NOTIFY tasksChanged) public: Backend(); - Q_INVOKABLE void addTodo(QString newTodo, QString date); Q_INVOKABLE void addTodoFromRawFormat(QString filePath, QString text, QString date); Q_INVOKABLE void addTagFilter(QString tag); Q_INVOKABLE void removeTagFilter(QString tag); - Q_INVOKABLE void addFileFilter(QString fileName); - Q_INVOKABLE void removeFileFilter(QString fileName); + Q_INVOKABLE void addResourceFilter(QString fileName); + Q_INVOKABLE void removeResourceFilter(QString fileName); Q_INVOKABLE void removeFilters(); Q_INVOKABLE void updateTodoFromRawFormat(int todoIndex, QString text, QString date); Q_INVOKABLE void updateTodo(int todoIndex, QString state, QString text, QString date); @@ -62,10 +61,10 @@ class Backend : public QObject void rebuildQMLTasksList(); QVariant getTasks(); - QVariant getFiles(); + QVariant getResources(); QVariant getTags(); QVariant getActiveTagsFilter(); - QVariant getActiveFilesFilter(); + QVariant getActiveResourcesFilter(); bool shouldHideCompletedTasks(); void saveConfig(); @@ -74,10 +73,10 @@ class Backend : public QObject mirai::TasksView todoView; QList QMLTasks; - QList QMLTasksFiles; + QList QMLResources; QList QMLTags; QList QMLActiveTagsFilter; - QList QMLActiveFilesFilter; + QList QMLActiveResourcesFilter; QMap tagsConfig; bool shouldHideCompletedTasks_ = true; diff --git a/src/TasksFile.cpp b/src/TasksFile.cpp index 1f89de5..93118f9 100644 --- a/src/TasksFile.cpp +++ b/src/TasksFile.cpp @@ -5,6 +5,7 @@ */ #include "TasksFile.h" +#include "core/BaseFileResource.h" QString QMLTasksFile::getName() { @@ -13,5 +14,9 @@ QString QMLTasksFile::getName() QString QMLTasksFile::getPath() { - return QString::fromStdString(tasksFile->getPath()); + auto fileResource = dynamic_cast(tasksFile); + if (!fileResource) { + return ""; + } + return QString::fromStdString(fileResource->getPath()); } diff --git a/src/TasksFile.h b/src/TasksFile.h index 8e7aac5..d7840fd 100644 --- a/src/TasksFile.h +++ b/src/TasksFile.h @@ -8,11 +8,10 @@ #define QML_TASKSFILE_H #include "QtCore/qvariant.h" +#include "core/BaseFileResource.h" #include #include -#include "core/TasksFile.h" - struct QMLTasksFile { Q_GADGET @@ -24,7 +23,7 @@ struct QMLTasksFile { QString getName(); QString getPath(); - mirai::TasksFile *tasksFile; + mirai::BaseResource *tasksFile; }; #endif diff --git a/src/core/BaseFileResource.h b/src/core/BaseFileResource.h new file mode 100644 index 0000000..37c26d8 --- /dev/null +++ b/src/core/BaseFileResource.h @@ -0,0 +1,42 @@ +/* + * Mirai. Copyright (C) 2024 Vyn + * This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only) + * The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt + */ + +#ifndef MIRAI_BASE_FILE_RESOURCE_H +#define MIRAI_BASE_FILE_RESOURCE_H + +#include "core/BaseResource.h" +#include + +namespace mirai +{ + +struct BaseFileResourceConstructor { + std::string name; + std::string path; +}; + +class BaseFileResource : public BaseResource +{ + public: + BaseFileResource(BaseFileResourceConstructor data) : BaseResource(data.name), path(data.path){}; + + BaseFileResource(BaseFileResource &) = delete; + BaseFileResource(BaseFileResource &&) = delete; + BaseFileResource operator=(BaseFileResource &) = delete; + BaseFileResource operator=(BaseFileResource &&) = delete; + + ~BaseFileResource() override = default; + + const std::string &getPath() const + { + return path; + } + + private: + std::string path; +}; +} // namespace mirai +#endif diff --git a/src/core/BaseResource.cpp b/src/core/BaseResource.cpp new file mode 100644 index 0000000..6ab5866 --- /dev/null +++ b/src/core/BaseResource.cpp @@ -0,0 +1,52 @@ +/* + * Mirai. Copyright (C) 2024 Vyn + * This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only) + * The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt + */ + +#include "BaseResource.h" +#include "TaskItem.h" +#include +#include +#include +#include +#include + +namespace mirai +{ + +void BaseResource::setDirty(bool shouldBeDirty) +{ + isDirty_ = shouldBeDirty; +} + +bool BaseResource::isDirty() const +{ + return isDirty_; +} + +std::vector> &BaseResource::getTasks() +{ + return tasks; +} + +void BaseResource::addTask(TaskItemData taskItem) +{ + tasks.push_back(std::make_unique(this, taskItem)); + setDirty(true); +} + +void BaseResource::removeTask(const TaskItem *taskToRemove) +{ + auto findFunction = [&](const std::unique_ptr &taskInFilter) { + return taskInFilter.get() == taskToRemove; + }; + auto taskToDelete = std::remove_if(tasks.begin(), tasks.end(), findFunction); + if (taskToDelete == tasks.end()) { + return; + } + tasks.erase(taskToDelete); + setDirty(true); +} + +} // namespace mirai diff --git a/src/core/BaseResource.h b/src/core/BaseResource.h new file mode 100644 index 0000000..2c7646a --- /dev/null +++ b/src/core/BaseResource.h @@ -0,0 +1,54 @@ +/* + * Mirai. Copyright (C) 2024 Vyn + * This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only) + * The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt + */ + +#ifndef MIRAI_BASE_RESOURCE_H +#define MIRAI_BASE_RESOURCE_H + +#include "core/TaskItem.h" +#include +#include +#include + +namespace mirai +{ + +class BaseResource +{ + public: + BaseResource(std::string name) : name(name){}; + BaseResource(BaseResource &) = delete; + BaseResource(BaseResource &&) = delete; + BaseResource operator=(BaseResource &) = delete; + BaseResource operator=(BaseResource &&) = delete; + + virtual ~BaseResource() = default; + virtual void save() = 0; + virtual void load() = 0; + + void setName(std::string name) + { + this->name = name; + } + + const std::string &getName() const + { + return name; + } + + void addTask(TaskItemData TaskItem); + void removeTask(const TaskItem *taskToRemove); + std::vector> &getTasks(); + + void setDirty(bool shouldBeDirty); + bool isDirty() const; + + private: + std::string name; + std::vector> tasks; + bool isDirty_ = false; +}; +} // namespace mirai +#endif diff --git a/src/core/Mirai.cpp b/src/core/Mirai.cpp index 6f38599..d41d964 100644 --- a/src/core/Mirai.cpp +++ b/src/core/Mirai.cpp @@ -6,7 +6,6 @@ #include "Mirai.h" #include "TaskItem.h" -#include "core/TasksFile.h" #include "cpp-utils/debug.h" #include "utils.h" #include @@ -19,70 +18,53 @@ namespace mirai { -void Mirai::loadFile(const std::string &path) +void Mirai::loadResource(std::unique_ptr &&resource) { - try { - auto tasksFile = TodoMdFormat::readFile(path); - files.push_back(std::move(tasksFile)); - reloadTags(); - } catch (std::exception &e) { - std::cout << "Cannot load file " << path << " : " << e.what() << std::endl; - } -} + resource->load(); + resources.push_back(std::move(resource)); + reloadTags(); +}; -void Mirai::unloadAllFiles() +void Mirai::unloadAllResources() { - files.clear(); + resources.clear(); } void Mirai::save() { - for (auto &file : files) { - if (file->isDirty()) { - file->sortByDate(); - TodoMdFormat::writeFile(*file); - file->setDirty(false); + for (auto &resource : resources) { + if (resource->isDirty()) { + resource->save(); + resource->setDirty(false); } } reloadTags(); } -void Mirai::addTask(TaskItemData taskItem) -{ - files[0]->addTask(taskItem); -} - -void Mirai::addTask(std::string text, std::string date) -{ - /*std::time_t t = std::time(nullptr);*/ - /*std::tm tm = *std::localtime(&t);*/ - /*std::stringstream ssCreationDate;*/ - /*ssCreationDate << std::put_time(&tm, "%Y-%m-%d");*/ - files[0]->addTask(text, date); -} - void Mirai::removeTask(const TaskItem *taskItem) { - for (auto &file : files) { - file->removeTask(taskItem); + for (auto &resource : resources) { + resource->removeTask(taskItem); } } -std::vector> &Mirai::getFiles() +std::vector> &Mirai::getResources() { - return files; + return resources; } -std::optional> Mirai::getFileByPath(const std::string &path) +std::optional> Mirai::getResourceByName(const std::string &name +) { - auto fileIterator = std::ranges::find_if(files, [&](const std::unique_ptr &file) { - return file->getPath() == path; - }); + auto resourceIterator = + std::ranges::find_if(resources, [&](const std::unique_ptr &resource) { + return resource->getName() == name; + }); - if (fileIterator == files.end()) { + if (resourceIterator == resources.end()) { return std::nullopt; } - return *(fileIterator->get()); + return *(resourceIterator->get()); } const std::vector &Mirai::getTags() @@ -94,8 +76,8 @@ void Mirai::reloadTags() { cpputils::debug::Timer reloadingTagsDuration; tags.clear(); - for (auto &file : files) { - for (auto &task : file->getTasks()) { + for (auto &resource : resources) { + for (auto &task : resource->getTasks()) { for (auto &tag : task->getTags()) { if (!vectorUtils::contains(tags, tag)) { tags.push_back(tag); diff --git a/src/core/Mirai.h b/src/core/Mirai.h index a3b9f3d..b93e9c0 100644 --- a/src/core/Mirai.h +++ b/src/core/Mirai.h @@ -8,8 +8,8 @@ #define MIRAI_MIRAI_H #include "TaskItem.h" -#include "TasksFile.h" #include "TodoMd.h" +#include "core/BaseFileResource.h" #include #include #include @@ -23,22 +23,19 @@ class Mirai { public: - void loadFile(const std::string &path); - void unloadAllFiles(); + void loadResource(std::unique_ptr &&resource); + void unloadAllResources(); void save(); - void addTask(TaskItemData taskItem); - void addTask(std::string text, std::string date); void removeTask(const TaskItem *taskItem); - void addFile(TasksFileConfig config); - std::optional> getFileByPath(const std::string &path); + std::optional> getResourceByName(const std::string &name); - std::vector> &getFiles(); + std::vector> &getResources(); const std::vector &getTags(); void reloadTags(); private: - std::vector> files = std::vector>(); + std::vector> resources; std::vector tags; }; } // namespace mirai diff --git a/src/core/StdFileResource.h b/src/core/StdFileResource.h new file mode 100644 index 0000000..0f9986d --- /dev/null +++ b/src/core/StdFileResource.h @@ -0,0 +1,63 @@ +/* + * Mirai. Copyright (C) 2024 Vyn + * This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only) + * The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt + */ + +#ifndef MIRAI_STD_FILE_RESOURCE_H +#define MIRAI_STD_FILE_RESOURCE_H + +#include "core/BaseFileResource.h" +#include "core/TodoMd.h" +#include +#include + +namespace mirai +{ + +class StdFileResource : public BaseFileResource +{ + public: + StdFileResource(BaseFileResourceConstructor data) : BaseFileResource(data){}; + StdFileResource(StdFileResource &) = delete; + StdFileResource(StdFileResource &&) = delete; + StdFileResource operator=(StdFileResource &) = delete; + StdFileResource operator=(StdFileResource &&) = delete; + + ~StdFileResource() override = default; + + void save() override + { + std::ofstream file(getPath()); + if (!file.is_open()) { + throw std::runtime_error("can't create " + getPath()); + } + + const std::string content = TodoMdFormat::stringifyTasks(getName(), getTasks()); + + file << content; + file.close(); + }; + + void load() override + { + std::ifstream file(getPath()); + if (!file.is_open()) { + return; + } + std::string content = ""; + std::string line; + + while (std::getline(file, line)) { + content += line + "\n"; + } + file.close(); + auto result = TodoMdFormat::parse(content); + for (const auto &task : result.tasks) { + addTask(task); + } + setName(result.name); + }; +}; +} // namespace mirai +#endif diff --git a/src/core/TaskItem.cpp b/src/core/TaskItem.cpp index ca71401..a55cecf 100644 --- a/src/core/TaskItem.cpp +++ b/src/core/TaskItem.cpp @@ -5,14 +5,14 @@ */ #include "TaskItem.h" -#include "TasksFile.h" +#include "BaseResource.h" #include "core/utils.h" #include namespace mirai { -TaskItem::TaskItem(TasksFile *parent, TaskItemData data) : parent(parent), data(data) +TaskItem::TaskItem(BaseResource *parent, TaskItemData data) : parent(parent), data(data) { } diff --git a/src/core/TaskItem.h b/src/core/TaskItem.h index 6f7beb5..8326200 100644 --- a/src/core/TaskItem.h +++ b/src/core/TaskItem.h @@ -24,15 +24,15 @@ struct TaskItemData { Tags tags; }; -class TasksFile; +class BaseResource; class TaskItem { - friend TasksFile; + friend BaseResource; private: public: - TaskItem(TasksFile *parent, const TaskItemData data); + TaskItem(BaseResource *parent, const TaskItemData data); TaskItem &operator=(const TaskItemData &newData) { @@ -62,7 +62,7 @@ class TaskItem private: void onChange(); - TasksFile *parent; + BaseResource *parent; TaskItemData data; }; } // namespace mirai diff --git a/src/core/TasksFile.cpp b/src/core/TasksFile.cpp deleted file mode 100644 index 3598182..0000000 --- a/src/core/TasksFile.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Mirai. Copyright (C) 2024 Vyn - * This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only) - * The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt - */ - -#include "TasksFile.h" -#include "TaskItem.h" -#include -#include -#include -#include -#include - -namespace mirai -{ - -TasksFile::TasksFile(TasksFileConstructor params) : name(params.name), path(params.path) -{ - for (const auto &task : params.tasks) { - auto taskPtr = std::make_unique(task); - tasks.push_back(std::move(taskPtr)); - } - sortByDate(); -} - -void TasksFile::onTaskChanged(TaskItem &taskItem) -{ - std::cout << "THIS PTR: " << this << std::endl; - setDirty(true); -} - -void TasksFile::setDirty(bool shouldBeDirty) -{ - isDirty_ = shouldBeDirty; -} - -bool TasksFile::isDirty() const -{ - return isDirty_; -} - -const std::string &TasksFile::getName() const -{ - return name; -} - -const std::string &TasksFile::getPath() const -{ - return path; -} - -TasksPtrs &TasksFile::getTasks() -{ - return tasks; -} - -void TasksFile::addTask(TaskItemData taskItem) -{ - tasks.push_back(std::make_unique(this, taskItem)); - setDirty(true); -} - -void TasksFile::addTask(std::string text, std::string date) -{ - auto newTask = std::make_unique( - TaskItem{this, {.text = text, .state = TODO, .date = date == "" ? "No date" : date}} - ); - tasks.push_back(std::move(newTask)); -} - -void TasksFile::removeTask(const TaskItem *taskToRemove) -{ - auto findFunction = [&](const std::unique_ptr &taskInFilter) { - return taskInFilter.get() == taskToRemove; - }; - auto taskToDelete = std::remove_if(tasks.begin(), tasks.end(), findFunction); - if (taskToDelete == tasks.end()) { - return; - } - tasks.erase(taskToDelete); - setDirty(true); -} - -void TasksFile::sortByDate() -{ - std::sort( - tasks.begin(), tasks.end(), - [](const std::unique_ptr &t1, const std::unique_ptr &t2) { - if (t1->hasDate() && !t2->hasDate()) { - return true; - } else if (!t1->hasDate() && t2->hasDate()) { - return false; - } - return t1->getDate() < t2->getDate(); - } - ); -} -} // namespace mirai diff --git a/src/core/TasksFile.h b/src/core/TasksFile.h deleted file mode 100644 index bd4ba8a..0000000 --- a/src/core/TasksFile.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Mirai. Copyright (C) 2024 Vyn - * This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only) - * The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt - */ - -#ifndef MIRAI_TASKSFILE_H -#define MIRAI_TASKSFILE_H - -#include "TaskItem.h" -#include -#include -#include - -namespace mirai -{ - -using TasksPtrs = std::vector>; - -struct TasksFileConfig { - std::string name; - std::string path; -}; - -struct TasksFileConstructor { - const std::string &name; - const std::string &path; - std::vector tasks; -}; - -class TasksFile -{ - - public: - TasksFile(const TasksFile &) = delete; - TasksFile &operator=(const TasksFile &) = delete; - TasksFile(TasksFileConstructor params); - void addTask(std::string text, std::string date); - void addTask(TaskItemData TaskItem); - void removeTask(const TaskItem *taskToRemove); - void sortByDate(); - - const std::string &getName() const; - const std::string &getPath() const; - TasksPtrs &getTasks(); - - void setDirty(bool shouldBeDirty); - bool isDirty() const; - - private: - void onTaskChanged(TaskItem &); - - std::string name; - std::string path; - TasksPtrs tasks; - bool isDirty_ = false; -}; -} // namespace mirai - -#endif diff --git a/src/core/TasksView.cpp b/src/core/TasksView.cpp index 25bf9c4..5e1dfc5 100644 --- a/src/core/TasksView.cpp +++ b/src/core/TasksView.cpp @@ -39,8 +39,9 @@ size_t TasksView::count() const void TasksView::update() { tasksToShow.clear(); - for (auto &file : mirai->getFiles()) { - if (filesFilter.size() > 0 && !vectorUtils::contains(filesFilter, file->getName())) { + for (auto &file : mirai->getResources()) { + if (resourcesFilter.size() > 0 && + !vectorUtils::contains(resourcesFilter, file->getName())) { continue; } for (auto &task : file->getTasks()) { @@ -88,16 +89,16 @@ void TasksView::removeTagFilter(const std::string &tag) update(); } -void TasksView::addFileFilter(const std::string &fileName) +void TasksView::addResourceFilter(const std::string &fileName) { - filesFilter.push_back(fileName); + resourcesFilter.push_back(fileName); update(); } -void TasksView::removeFileFilter(const std::string &fileName) +void TasksView::removeResourceFilter(const std::string &fileName) { - filesFilter.erase(std::remove_if( - filesFilter.begin(), filesFilter.end(), + resourcesFilter.erase(std::remove_if( + resourcesFilter.begin(), resourcesFilter.end(), [&](const auto &fileInFilter) { return fileName == fileInFilter; } @@ -118,6 +119,6 @@ const std::vector &TasksView::getActiveTagsFilter() const std::vector &TasksView::getActiveFilesFilter() { - return filesFilter; + return resourcesFilter; } } // namespace mirai diff --git a/src/core/TasksView.h b/src/core/TasksView.h index 1aa796c..a5b2fa8 100644 --- a/src/core/TasksView.h +++ b/src/core/TasksView.h @@ -6,11 +6,9 @@ #ifndef MIRAI_TASKSVIEW_H #define MIRAI_TASKSVIEW_H -#include "TasksFile.h" #include "core/Mirai.h" #include "using.h" #include -#include #include namespace mirai @@ -28,8 +26,8 @@ class TasksView void update(); void addTagFilter(const std::string &tag); void removeTagFilter(const std::string &tag); - void addFileFilter(const std::string &fileName); - void removeFileFilter(const std::string &fileName); + void addResourceFilter(const std::string &fileName); + void removeResourceFilter(const std::string &fileName); void removeFilters(); const Tags &getActiveTagsFilter(); const std::vector &getActiveFilesFilter(); @@ -38,7 +36,7 @@ class TasksView Mirai *mirai; std::vector tasksToShow; Tags tagsFilter; - std::vector filesFilter; + std::vector resourcesFilter; }; } // namespace mirai #endif diff --git a/src/core/TodoMd.cpp b/src/core/TodoMd.cpp index 0e3e614..4d161e4 100644 --- a/src/core/TodoMd.cpp +++ b/src/core/TodoMd.cpp @@ -6,7 +6,6 @@ #include "TodoMd.h" #include "TaskItem.h" -#include "core/TasksFile.h" #include "cpp-utils/debug.h" #include "utils.h" #include @@ -17,50 +16,6 @@ namespace mirai { -std::unique_ptr TodoMdFormat::readFile(const std::string &path) -{ - cpputils::debug::Timer readMdFormatDuration; - std::ifstream file(path); - if (!file.is_open()) { - throw std::runtime_error("todo.txt file not found"); - } - std::vector taskItems; - std::string line; - - if (!std::getline(file, line) || line.substr(0, 2) != "# ") { - std::cerr << "Couldn't find the task list name" << std::endl; - } - - std::string name = line.substr(2); - std::string currentDate = ""; - - auto tasksFile = std::make_unique( - TasksFileConstructor{.name = name, .path = path, .tasks = taskItems} - ); - - cpputils::debug::Timer stringToTaskDuration; - stringToTaskDuration.reset(); - cpputils::debug::Timer gelinesDuration; - while (std::getline(file, line)) { - if (std::string_view{line.data(), 3} == "## ") { - currentDate = line.substr(3); - } else if (std::string_view{line.data(), 5} == "- [ ]" || std::string_view{line.data(), 5} == "- [X]") { - stringToTaskDuration.start(); - TaskItemData taskItemData = stringToTask(line, currentDate); - stringToTaskDuration.stop(); - taskItemData.date = currentDate; - tasksFile->addTask(taskItemData); - } - } - - gelinesDuration.printTimeElapsed("getlinesDuration"); - stringToTaskDuration.printTimeElapsed("stringToTaskDuration"); - file.close(); - tasksFile->setDirty(false); - readMdFormatDuration.printTimeElapsed("Reading MD File duration of " + path); - return tasksFile; -} - Tags TodoMdFormat::extractTagsFromMetadata(std::string metadata) { Tags tags; @@ -77,25 +32,6 @@ Tags TodoMdFormat::extractTagsFromMetadata(std::string metadata) return tags; } -void TodoMdFormat::writeFile(TasksFile &tasks) -{ - std::ofstream file(tasks.getPath()); - if (!file.is_open()) { - throw std::runtime_error("can't create todo.txt"); - } - std::string currentDate = ""; - - file << "# " << tasks.getName() << "\n"; - for (const auto &task : tasks.getTasks()) { - if (currentDate != task->getDate()) { - currentDate = task->getDate(); - file << "\n## " << (task->getDate() != "" ? task->getDate() : "No date") << "\n\n"; - } - file << taskToString(*task) << '\n'; - } - file.close(); -} - std::string TodoMdFormat::fieldWithSpace(const std::string &field) { if (field.length() == 0) { diff --git a/src/core/TodoMd.h b/src/core/TodoMd.h index 2b7e0e7..48e8c00 100644 --- a/src/core/TodoMd.h +++ b/src/core/TodoMd.h @@ -8,17 +8,76 @@ #define MIRAI_TODOMD_H #include "TaskItem.h" -#include "TasksFile.h" +#include "cpp-utils/debug.h" +#include #include +#include namespace mirai { +struct MiraiMarkdownFormatParseResult { + std::string name; + std::vector tasks; +}; + class TodoMdFormat { public: - static std::unique_ptr readFile(const std::string &path); - static void writeFile(TasksFile &tasks); + static std::string + stringifyTasks(const std::string &name, const std::vector> &tasks) + { + std::string result = ""; + std::string currentDate = ""; + + result += "# " + name + "\n"; + for (const auto &task : tasks) { + if (currentDate != task->getDate()) { + currentDate = task->getDate(); + result += "\n## " + (task->getDate() != "" ? task->getDate() : "No date") + "\n\n"; + } + result += taskToString(*task) + '\n'; + } + return result; + }; + + static MiraiMarkdownFormatParseResult parse(const std::string &content) + { + cpputils::debug::Timer readMdFormatDuration; + std::vector taskItems; + std::string line; + + std::stringstream contentStream(content); + + if (!std::getline(contentStream, line) || line.substr(0, 2) != "# ") { + std::cerr << "Couldn't find the task list name" << std::endl; + } + + std::string name = line.substr(2); + std::string currentDate = ""; + + std::vector result; + + cpputils::debug::Timer stringToTaskDuration; + stringToTaskDuration.reset(); + cpputils::debug::Timer gelinesDuration; + while (std::getline(contentStream, line)) { + if (std::string_view{line.data(), 3} == "## ") { + currentDate = line.substr(3); + } else if (std::string_view{line.data(), 5} == "- [ ]" || std::string_view{line.data(), 5} == "- [X]") { + stringToTaskDuration.start(); + TaskItemData taskItemData = stringToTask(line, currentDate); + stringToTaskDuration.stop(); + taskItemData.date = currentDate; + result.push_back(taskItemData); + } + } + + gelinesDuration.printTimeElapsed("getlinesDuration"); + stringToTaskDuration.printTimeElapsed("stringToTaskDuration"); + readMdFormatDuration.printTimeElapsed("Reading MD File duration"); + return {.name = name, .tasks = result}; + } static std::string taskToString(const TaskItem &task); static TaskItemData stringToTask(const std::string &str, const std::string &date); diff --git a/src/qml/SideMenu.qml b/src/qml/SideMenu.qml index 75bb0e3..8f585a6 100644 --- a/src/qml/SideMenu.qml +++ b/src/qml/SideMenu.qml @@ -44,7 +44,7 @@ Rectangle { Rectangle { Layout.preferredHeight: childrenRect.height Layout.fillWidth: true - color: backend.activeFilesFilter.includes(modelData.name) ? MiraiColorPalette.filterSelected : mouse.hovered ? MiraiColorPalette.filterHovered : "transparent" + color: backend.activeResourcesFilter.includes(modelData.name) ? MiraiColorPalette.filterSelected : mouse.hovered ? MiraiColorPalette.filterHovered : "transparent" radius: 4 AppText { text: modelData.name @@ -53,10 +53,10 @@ Rectangle { MouseArea { anchors.fill: parent onClicked: { - if (backend.activeFilesFilter.includes(modelData.name)) { - backend.removeFileFilter(modelData.name) + if (backend.activeResourcesFilter.includes(modelData.name)) { + backend.removeResourceFilter(modelData.name) } else { - backend.addFileFilter(modelData.name) + backend.addResourceFilter(modelData.name) } } HoverHandler { diff --git a/src/qml/forms/FilesForm.qml b/src/qml/forms/FilesForm.qml index 3ac1a2f..49c92d2 100644 --- a/src/qml/forms/FilesForm.qml +++ b/src/qml/forms/FilesForm.qml @@ -44,8 +44,8 @@ ColumnLayout { id: fileDialog currentFolder: StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0] onAccepted: { - console.log(selectedFile) - internal.paths = [...internal.paths, selectedFile] + console.log(selectedFile.toString()) + internal.paths = [...internal.paths, selectedFile.toString().replace(/^file:\/\//, "")] } } diff --git a/tests/test.cpp b/tests/format.cpp similarity index 97% rename from tests/test.cpp rename to tests/format.cpp index 54569be..b931a85 100644 --- a/tests/test.cpp +++ b/tests/format.cpp @@ -4,8 +4,6 @@ * The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt */ -// TODO this is just a quick setup for later - #include "core/Mirai.h" #include "core/TaskItem.h" #include "core/TodoMd.h" diff --git a/tests/saving.cpp b/tests/saving.cpp new file mode 100644 index 0000000..8e4ddce --- /dev/null +++ b/tests/saving.cpp @@ -0,0 +1,48 @@ +/* + * Mirai. Copyright (C) 2024 Vyn + * This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only) + * The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt + */ + +#include "core/BaseFileResource.h" +#include "core/Mirai.h" +#include "core/StdFileResource.h" +#include "core/TaskItem.h" +#include "core/TodoMd.h" +#include +#include + +TEST_CASE("Saving data") +{ + SECTION("Saving using file resource") + { + { + mirai::Mirai mirai; + auto fileResource = std::make_unique( + mirai::BaseFileResourceConstructor{.name = "Testing", .path = "testing.md"} + ); + mirai.loadResource(std::move(fileResource)); + + auto task = mirai::TodoMdFormat::stringToTask( + "- [X] 08:00-10:00 > This is a test -- #mirai", "2024-04-19" + ); + mirai.getResources()[0]->addTask(task); + mirai.save(); + } + + { + mirai::Mirai mirai; + auto fileResource = std::make_unique( + mirai::BaseFileResourceConstructor{.name = "Testing", .path = "testing.md"} + ); + mirai.loadResource(std::move(fileResource)); + + const auto &resources = mirai.getResources(); + REQUIRE(resources.size() == 1); + REQUIRE(resources[0]->getName() == "Testing"); + REQUIRE(resources[0]->getTasks().size() == 1); + REQUIRE(resources[0]->getTasks().at(0)->getText() == "This is a test"); + REQUIRE(resources[0]->getTasks().at(0)->getDate() == "2024-04-19"); + } + } +}