/* * 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 "cpp-utils/debug.h" #include "cpp-utils/string.h" #include "mirai-core/DateTime.h" #include "mirai-core/Day.h" #include "mirai-core/Event.h" #include #include #include #include #include #include namespace mirai { struct MiraiMarkdownFormatParseResult { std::string name; std::vector days; }; class TodoMdFormat { public: static std::string stringify(const std::string &name, const std::vector> &days) { std::string result = "# " + name + "\n\n"; std::string currentDate = ""; for (const auto &day : days) { auto &date = day->getDate(); result += "## " + std::format("{:02d}-{:02d}-{:02d}", date.year, date.month, date.day) + "\n\n"; for (const auto &event : *(day->events())) { auto &start = event->getStartTime(); auto &end = event->getEndTime(); result += "> " + std::format( "{:02d}h{:02d}-{:02d}h{:02d} {}", start.hour, start.minute, end.hour, end.minute, event->getText() ) + "\n"; for (const auto &task : *(event->tasks())) { result += taskToString(*task) + '\n'; } result += '\n'; } for (const auto &task : *(day->tasks())) { result += taskToString(*task) + '\n'; } result += '\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 currentDateString = ""; std::vector daysData; mirai::DayData *currentDay = nullptr; mirai::EventData *currentEvent = nullptr; cpputils::debug::Timer stringToTaskDuration; stringToTaskDuration.reset(); cpputils::debug::Timer gelinesDuration; while (std::getline(contentStream, line)) { if (std::string_view{line.data(), 3} == "## ") { currentDateString = line.substr(3); auto dateOpt = mirai::stringToDate(currentDateString); if (!dateOpt.has_value()) { throw std::runtime_error("Malformated date"); } daysData.push_back({.date = dateOpt.value()}); currentDay = &daysData.back(); } else if (line.starts_with("> ")) { auto event = stringToEvent(line, currentDateString); if (currentDay) { currentDay->events.push_back(event); currentEvent = ¤tDay->events.back(); } } else if (line.starts_with("- [ ]") || line.starts_with("- [X]")) { stringToTaskDuration.start(); TaskItemData taskItemData = stringToTask(line, currentDateString); stringToTaskDuration.stop(); if (currentEvent) { currentEvent->tasks.push_back(taskItemData); } else if (currentDay) { currentDay->tasks.push_back(taskItemData); } } else if (cpputils::string::trim(line) == "") { currentEvent = nullptr; } } gelinesDuration.printTimeElapsed("getlinesDuration"); stringToTaskDuration.printTimeElapsed("stringToTaskDuration"); readMdFormatDuration.printTimeElapsed("Reading MD File duration"); return {.name = name, .days = daysData}; } static std::string taskToString(const TaskItem &task); static TaskItemData stringToTask(const std::string &str, const std::string &date); static EventData stringToEvent(const std::string &str, const std::string &date); private: static std::string fieldWithSpace(const std::string &field); static TaskItem parseLine(const std::string &line); static Tags extractTagsFromMetadata(std::string metadata); }; } // namespace mirai #endif