#include "converter.h" #include #include #include #include #include #include #include #include #include #include "typefactory.h" static std::string rstrip_cr (std::string s) { if (!s.empty() && s.back() == '\r') s.pop_back(); return s; } struct Quote { std::string meta; std::vector text_lines; }; struct ParseContext { std::vector order; std::unordered_map> by_title; std::unordered_map seen; std::string cur_title; std::string cur_meta; std::vector cur_text; void start_title (std::string title) { cur_title = std::move (title); cur_meta.clear(); cur_text.clear(); } void set_meta (std::string meta) { cur_meta = std::move (meta); } void add_text (std::string line) { cur_text.push_back (std::move (line)); } void finalize_quote() { if (cur_title.empty() || cur_meta.empty()) { cur_meta.clear(); cur_text.clear(); return; } if (seen.find (cur_title) == seen.end()) { seen.emplace (cur_title, order.size()); order.push_back (cur_title); } by_title[cur_title].push_back (Quote{cur_meta, cur_text}); cur_meta.clear(); cur_text.clear(); } }; enum class Stage { Title, Meta, Body }; static constexpr std::array kAllStages = { Stage::Title, Stage::Meta, Stage::Body }; struct ILineHandler { virtual ~ILineHandler() = default; virtual void handle (const std::string &line, ParseContext &ctx, Stage &next) = 0; }; struct TitleHandler final : ILineHandler { void handle (const std::string &line, ParseContext &ctx, Stage &next) override { if (line.empty()) { next = Stage::Title; return; } ctx.start_title (line); next = Stage::Meta; } }; struct MetaHandler final : ILineHandler { void handle (const std::string &line, ParseContext &ctx, Stage &next) override { ctx.set_meta (line); next = Stage::Body; } }; struct BodyHandler final : ILineHandler { void handle (const std::string &line, ParseContext &ctx, Stage &next) override { if (line == "==========") { ctx.finalize_quote(); next = Stage::Title; return; } ctx.add_text (line); next = Stage::Body; } }; using HandlerPtr = std::shared_ptr; using HandlerMap = std::unordered_map; static TypeFactory build_factory() { TypeFactory factory; factory.registerType (Stage::Title); factory.registerType (Stage::Meta); factory.registerType (Stage::Body); return factory; } static HandlerMap build_handlers_cache ( const TypeFactory &factory) { HandlerMap handlers; handlers.reserve (kAllStages.size()); for (Stage stage : kAllStages) { try { auto handler = factory.create (stage); if (!handler) throw std::runtime_error ("Factory returned null handler"); handlers.emplace (stage, std::move (handler)); } catch (const std::out_of_range &) { throw std::runtime_error ("Missing handler registration for Stage"); } } if (handlers.size() != kAllStages.size()) throw std::runtime_error ("Handler cache size mismatch"); return handlers; } struct Converter::Impl { ParseContext context; }; Converter::Converter (std::string input_file_name) : input_file_name_ (std::move (input_file_name)) , impl_ (std::make_unique()) { } Converter::~Converter() = default; int Converter::error_code() const noexcept { return error_code_; } void Converter::set_error_code (int error_code) noexcept { error_code_ = error_code; } const std::vector &Converter::titles() const noexcept { return impl_->context.order; } std::size_t Converter::quote_count (const std::string &title) const { const auto it = impl_->context.by_title.find (title); return it == impl_->context.by_title.end() ? 0 : it->second.size(); } const std::string &Converter::quote_meta (const std::string &title, std::size_t index) const { return impl_->context.by_title.at (title).at (index).meta; } const std::vector &Converter::quote_text_lines ( const std::string &title, std::size_t index) const { return impl_->context.by_title.at (title).at (index).text_lines; } bool Converter::parse() { std::ifstream input (input_file_name_); if (!input.is_open()) { std::cerr << "Failed to open input file: " << input_file_name_ << " (" << std::strerror (errno) << ")\n"; error_code_ = 2; return false; } TypeFactory factory = build_factory(); HandlerMap handlers; try { handlers = build_handlers_cache (factory); } catch (const std::exception &e) { std::cerr << "Internal error while building handler cache: " << e.what() << "\n"; error_code_ = 4; return false; } impl_->context = ParseContext{}; Stage stage = Stage::Title; std::string line; while (std::getline (input, line)) { line = rstrip_cr (std::move (line)); auto it = handlers.find (stage); if (it == handlers.end() || !it->second) { std::cerr << "Internal error: missing handler at runtime\n"; error_code_ = 4; return false; } Stage next = stage; it->second->handle (line, impl_->context, next); stage = next; } impl_->context.finalize_quote(); error_code_ = 0; return true; }