first commit

This commit is contained in:
Vyn 2024-04-10 16:53:18 +02:00
commit 3e7d8b4b70
43 changed files with 2681 additions and 0 deletions

55
src/core/Mirai.cpp Normal file
View file

@ -0,0 +1,55 @@
/*
* 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 "Mirai.h"
namespace mirai {
void Mirai::loadFile(const std::string& path) {
auto tasksFile = TodoMdFormat::readFile(path);
files->push_back(std::move(tasksFile));
tags.clear();
for (auto& file : *files) {
for (auto& tag : file.getTags()) {
tags.push_back(tag);
}
}
}
void Mirai::save() {
for (auto& file : *files) {
TodoMdFormat::writeFile(file);
}
}
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);
for (auto& view : views) {
view->update();
}
}
void Mirai::removeTask(const TaskItem* taskItem) {
(*files)[0].removeTask(taskItem);
for (auto& view : views) {
view->update();
}
}
std::weak_ptr<TasksView> Mirai::getTasks() {
auto view = std::make_shared<TasksView>(files);
views.push_back(view);
return view;
}
const std::vector<std::string>& Mirai::getTags() {
return tags;
}
}

45
src/core/Mirai.h Normal file
View file

@ -0,0 +1,45 @@
/*
* 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_MIRAI_H
#define MIRAI_MIRAI_H
#include "TaskItem.h"
#include "TasksFile.h"
#include "TasksView.h"
#include "TodoMd.h"
#include <algorithm>
#include <memory>
namespace mirai {
class Mirai {
public:
void loadFile(const std::string& path);
void save();
void addTask(std::string text, std::string date);
void removeTask(const TaskItem* taskItem);
std::weak_ptr<TasksView> getTasks();
const std::vector<std::string>& getTags();
private:
// The `TasksFile`s are shared to the views, their lifetime can outlive
// this (Mirai) object
// because we can't control if the caller will keep the main object alive.
std::shared_ptr<std::vector<TasksFile>> files = std::make_shared<std::vector<TasksFile>>();
// We keep a vector of shared_ptr because we need the ref counting mechanism to know
// if we have to remove the view (not used) or tell the view to update() on
// some changes.
std::vector<std::shared_ptr<TasksView>> views;
std::vector<std::string> tags;
};
}
#endif

32
src/core/TaskItem.cpp Normal file
View file

@ -0,0 +1,32 @@
/*
* 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 "TaskItem.h"
namespace mirai {
void TaskItem::markAsDone() {
state = DONE;
}
void TaskItem::markAsUndone() {
state = TODO;
}
void TaskItem::setDate(const std::string& date) {
this->date = date;
}
void TaskItem::setText(const std::string& text) {
this->text = text;
}
const std::string& TaskItem::getText() const { return text; }
const TaskItemState& TaskItem::getState() const { return state; }
const std::string& TaskItem::getDate() const { return date; }
bool TaskItem::hasDate() const { return isDate(date); }
}

49
src/core/TaskItem.h Normal file
View file

@ -0,0 +1,49 @@
/*
* 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_TASKITEM_H
#define MIRAI_TASKITEM_H
#include "utils.h"
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <memory>
#include <regex>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
namespace mirai {
enum TaskItemState {
TODO,
DONE
};
struct TaskItem {
public:
void markAsDone();
void markAsUndone();
void setDate(const std::string& date);
void setText(const std::string& text);
const std::string& getText() const;
const TaskItemState& getState() const;
const std::string& getDate() const;
bool hasDate() const;
std::string text;
TaskItemState state;
std::string date;
};
}
#endif

71
src/core/TasksFile.cpp Normal file
View file

@ -0,0 +1,71 @@
/*
* 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 "using.h"
namespace mirai {
TasksFile::TasksFile(TasksFileConstructor params) : name(params.name), path(params.path) {
for (const auto& task : params.tasks) {
processTaskMetaData(task);
tasks.push_back(std::make_unique<TaskItem>(task));
}
sortByDate();
}
const std::string& TasksFile::getName() const { return name; }
const std::string& TasksFile::getPath() const { return path; }
TasksPtrs& TasksFile::getTasks() { return tasks; }
Tags& TasksFile::getTags() { return tags; }
void TasksFile::addTask(std::string text, std::string date) {
auto newTask = std::make_unique<TaskItem>(TaskItem{
.text = text,
.state = TODO,
.date = date == "" ? "No date" : date
});
tasks.push_back(std::move(newTask));
sortByDate();
}
void TasksFile::removeTask(const TaskItem* taskToRemove) {
tasks.erase(std::remove_if(tasks.begin(), tasks.end(), [&] (const std::unique_ptr<TaskItem>& taskInFilter){
return taskInFilter.get() == taskToRemove;
}));
}
void TasksFile::processTaskMetaData(const TaskItem& task) {
const std::string metaDataSeparator = " -- ";
auto separatorIndex = task.text.find(metaDataSeparator);
if (separatorIndex == std::string::npos) {
return;
}
auto metaData = task.text.substr(separatorIndex + metaDataSeparator.length());
std::regex regex("(#[a-zA-Z0-1]+)");
std::smatch matches;
while (std::regex_search(metaData, matches, regex))
{
if (std::find(tags.begin(), tags.end(), matches[0]) == tags.end()) {
tags.push_back(matches[0]);
}
metaData = matches.suffix();
}
}
void TasksFile::sortByDate() {
std::sort(tasks.begin(), tasks.end(), [] (const std::unique_ptr<TaskItem>& t1, const std::unique_ptr<TaskItem>& t2) {
if (t1->hasDate() && !t2->hasDate()) {
return true;
} else if (!t1->hasDate() && t2->hasDate()) {
return false;
}
return t1->date < t2->date;
});
}
}

58
src/core/TasksFile.h Normal file
View file

@ -0,0 +1,58 @@
/*
* 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 <algorithm>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <memory>
#include <ostream>
#include <regex>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
#include "TaskItem.h"
#include "using.h"
#include "utils.h"
namespace mirai {
struct TasksFileConstructor {
const std::string& name;
const std::string& path;
std::vector<TaskItem> tasks;
};
class TasksFile {
public:
TasksFile(TasksFileConstructor params);
void addTask(std::string text, std::string date);
void removeTask(const TaskItem* taskToRemove);
void processTaskMetaData(const TaskItem& task);
void sortByDate();
const std::string& getName() const;
const std::string& getPath() const;
TasksPtrs& getTasks();
Tags& getTags();
private:
std::string name;
std::string path;
TasksPtrs tasks;
Tags tags;
};
}
#endif

56
src/core/TasksView.cpp Normal file
View file

@ -0,0 +1,56 @@
/*
* 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 "TasksView.h"
namespace mirai {
TasksView::TasksView(std::shared_ptr<std::vector<TasksFile>> files) : files(files) {
update();
}
TaskItem& TasksView::operator[](int index) {
return *(tasksToShow.at(index));
}
int TasksView::count() const { return tasksToShow.size(); }
void TasksView::update() {
tasksToShow.clear();
for (auto& file : *files) {
for (auto& task : file.getTasks()) {
if (tagsFilter.size() != 0
&& std::find_if(tagsFilter.begin(), tagsFilter.end(), [&](const std::string& tag) {
return task->getText().find(tag) != std::string::npos;
}) == tagsFilter.end())
continue;
tasksToShow.push_back(task.get());
}
}
}
void TasksView::addTagFilter(const std::string& tag) {
tagsFilter.push_back(tag);
update();
}
void TasksView::removeTagFilter(const std::string& tag) {
tagsFilter.erase(std::remove_if(tagsFilter.begin(), tagsFilter.end(), [&] (const auto& tagInFilter){
return tag == tagInFilter;
}));
update();
}
void TasksView::removeFilters() {
tagsFilter.clear();
update();
}
const std::vector<std::string>& TasksView::getActiveTagsFilter() {
return tagsFilter;
}
}

41
src/core/TasksView.h Normal file
View file

@ -0,0 +1,41 @@
/*
* 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_TASKSVIEW_H
#define MIRAI_TASKSVIEW_H
#include "TasksFile.h"
#include "using.h"
#include <algorithm>
#include <iostream>
#include <memory>
#include <ostream>
#include <string>
namespace mirai {
class TasksView {
public:
TasksView(std::shared_ptr<std::vector<TasksFile>> files);
TaskItem& operator[](int index);
int count() const;
void update();
void addTagFilter(const std::string& tag);
void removeTagFilter(const std::string& tag);
void removeFilters();
const Tags& getActiveTagsFilter();
private:
std::shared_ptr<std::vector<TasksFile>> files;
std::vector<TaskItem*> tasksToShow;
Tags tagsFilter;
};
}
#endif

78
src/core/TodoMd.cpp Normal file
View file

@ -0,0 +1,78 @@
/*
* 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 "TodoMd.h"
namespace mirai {
TasksFile TodoMdFormat::readFile(const std::string& path) {
std::ifstream file(path);
if (!file.is_open()) {
throw std::runtime_error("todo.txt file not found");
}
std::vector<TaskItem> 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 = "";
while (std::getline(file, line)) {
if (line.substr(0, 3) == "## ") {
currentDate = line.substr(3);
} else if (line.substr(0, 5) == "- [ ]" || line.substr(0, 5) == "- [X]") {
std::string text = line.substr(5);
trim(text);
TaskItem taskItem = {
.text = text,
.state = line.substr(0, 5) == "- [X]" ? DONE : TODO,
.date = currentDate
};
taskItems.push_back(taskItem);
}
}
file.close();
TasksFile tasks({
.name = name,
.path = path,
.tasks = taskItems
});
return tasks;
}
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->date) {
currentDate = task->date;
file << "\n## " << (task->date != "" ? task->date : "No date") << "\n\n";
}
if (task->getState() == TODO) {
file << "- [ ] ";
} else if (task->getState() == DONE) {
file << "- [X] ";
}
file << task->getText() << '\n';
}
file.close();
}
std::string TodoMdFormat::fieldWithSpace(const std::string& field) {
if (field.length() == 0)
return "";
return (field + " ");
}
}

38
src/core/TodoMd.h Normal file
View file

@ -0,0 +1,38 @@
/*
* 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_TODOMD_H
#define MIRAI_TODOMD_H
#include "TaskItem.h"
#include "TasksFile.h"
#include "utils.h"
#include <fstream>
#include <iostream>
#include <iterator>
#include <memory>
#include <ostream>
#include <regex>
#include <stdexcept>
#include <string>
#include <vector>
namespace mirai {
class TodoMdFormat {
public:
static TasksFile readFile(const std::string& path);
static void writeFile(TasksFile& tasks);
private:
static std::string fieldWithSpace(const std::string& field);
static TaskItem parseLine(const std::string& line);
};
}
#endif

95
src/core/TodoTxt.h Normal file
View file

@ -0,0 +1,95 @@
/*
* 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_TODOTXT_H
#define MIRAI_TODOTXT_H
#include "TaskItem.h"
#include "utils.h"
#include <fstream>
#include <iostream>
#include <iterator>
#include <memory>
#include <regex>
#include <stdexcept>
#include <string>
#include <vector>
namespace mirai {
class TodoTxtFormat {
public:
static TaskList readFile() {
std::ifstream file("../newqml/todo.txt");
if (!file.is_open()) {
throw std::runtime_error("todo.txt file not found");
}
std::vector<TaskItem> taskItems;
std::string line;
while (std::getline(file, line)) {
auto taskItem = parseLine(line);
taskItems.push_back(taskItem);
}
file.close();
TaskList tasks({
.tasks = taskItems
});
return tasks;
}
static void writeFile(TaskList& tasks) {
std::ofstream file("../newqml/todo.txt");
if (!file.is_open()) {
throw std::runtime_error("can't create todo.txt");
}
for (const auto& task : tasks.getTasks()) {
file << fieldWithSpace(task->state == DONE ? "x" : task->priority);
file << fieldWithSpace(task->state == DONE ? task->getDate() : "");
file << fieldWithSpace(task->getCreationDate());
file << task->getText() << '\n';
}
file.close();
}
private:
static std::string fieldWithSpace(const std::string& field) {
if (field.length() == 0)
return "";
return (field + " ");
}
static TaskItem parseLine(const std::string& line) {
std::smatch matches;
std::regex regex("(^x )?(\\([A-Z]\\) )?([0-9]{4}-[0-9]{2}-[0-9]{2} )?([0-9]{4}-[0-9]{2}-[0-9]{2} )?(.*)");
std::regex_match(line, matches, regex);
for (size_t i = 0; i < matches.size(); ++i) {
std::ssub_match sub_match = matches[i];
std::string piece = sub_match.str();
}
const TaskItemState taskState = trim_copy(matches[1].str()) == "x" ? DONE : TODO;
const std::string priority = trim_copy(matches[2].str());
const std::string firstDate = trim_copy(matches[3].str());
const std::string secondDate = trim_copy(matches[4].str());
const std::string text = trim_copy(matches[5].str());
const std::string date = taskState == DONE ? firstDate : "";
return {
.text = text,
.state = taskState,
.date = date,
};
}
};
}
#endif

20
src/core/using.h Normal file
View file

@ -0,0 +1,20 @@
/*
* 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_USING_H
#define MIRAI_USING_H
#include "TaskItem.h"
#include <memory>
#include <string>
#include <vector>
namespace mirai {
using Tags = std::vector<std::string>;
using TasksPtrs = std::vector<std::unique_ptr<TaskItem>>;
}
#endif

44
src/core/utils.cpp Normal file
View file

@ -0,0 +1,44 @@
/*
* 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 "utils.h"
void ltrim(std::string& s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
return !std::isspace(ch);
}));
}
void rtrim(std::string& s) {
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
return !std::isspace(ch);
}).base(), s.end());
}
void trim(std::string& s) {
rtrim(s);
ltrim(s);
}
std::string ltrim_copy(std::string s) {
ltrim(s);
return s;
}
std::string rtrim_copy(std::string s) {
rtrim(s);
return s;
}
std::string trim_copy(std::string s) {
trim(s);
return s;
}
bool isDate(const std::string& dateStr) {
std::regex regex("[0-9]{4}-[0-9]{2}-[0-9]{2}");
return std::regex_match(dateStr, regex);
}

25
src/core/utils.h Normal file
View file

@ -0,0 +1,25 @@
/*
* 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_UTILS_H
#define MIRAI_UTILS_H
#include <algorithm>
#include <cctype>
#include <locale>
#include <regex>
void ltrim(std::string& s);
void rtrim(std::string& s);
void trim(std::string& s);
std::string ltrim_copy(std::string s);
std::string rtrim_copy(std::string s);
std::string trim_copy(std::string s);
bool isDate(const std::string& dateStr);
#endif