/* * 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" #include "TaskItem.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 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]") { TaskItem taskItem = StringToTask(line, currentDate); taskItem.setDate(currentDate); taskItems.push_back(taskItem); } } file.close(); TasksFile tasks({ .name = name, .path = path, .tasks = taskItems }); return tasks; } Tags TodoMdFormat::extractTagsFromMetadata(std::string metadata) { Tags tags; 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(); } 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->date) { currentDate = task->date; file << "\n## " << (task->date != "" ? task->date : "No date") << "\n\n"; } file << TaskToString(*task) << '\n'; } file.close(); } std::string TodoMdFormat::fieldWithSpace(const std::string& field) { if (field.length() == 0) return ""; return (field + " "); } TaskItem TodoMdFormat::StringToTask(const std::string& str, const std::string& date) { std::smatch matches; std::regex regex("- \\[(\\s|X)\\] (([0-9]{2}:[0-9]{2})-([0-9]{2}:[0-9]{2}) > )?(.*?)( -- (.*))?"); std::regex_match(str, matches, regex); std::cout << "line " << str << std::endl; std::cout << "M 0 " << matches[0] << std::endl; std::cout << "M 1 " << matches[1] << std::endl; std::cout << "M 2 " << matches[2] << std::endl; std::cout << "M 3 " << matches[3] << std::endl; std::cout << "M 4 " << matches[4] << std::endl; std::cout << "M 5 " << matches[5] << std::endl; std::cout << "M 6 " << matches[6] << std::endl; std::cout << "M 7 " << matches[7] << std::endl; std::string text = matches[5]; trim(text); TaskItem taskItem = { .text = text, .state = str.substr(0, 5) == "- [X]" ? DONE : TODO, .date = date, .startTime = matches[3], .endTime = matches[4], .tags = extractTagsFromMetadata(matches[7]) }; return taskItem; } std::string TodoMdFormat::TaskToString(const TaskItem& task) { std::string str = task.getText(); if (task.getTags().size() > 0) { str += " --"; for (const std::string& tag : task.getTags()) { str += " " + tag; } } if (task.getStartTime() != "" && task.getEndTime() != "") { str = task.getStartTime() + "-" + task.getEndTime() + " > " + str; } str = (task.getState() == DONE ? "- [X] " : "- [ ] ") + str; return str; } }