diff --git a/typefactory/main.cpp b/typefactory/main.cpp index 341f237..61edaf1 100644 --- a/typefactory/main.cpp +++ b/typefactory/main.cpp @@ -10,7 +10,7 @@ #include #include -#include "typefactory.h" // <-- твой хедер +#include "typefactory.h" // ------------------------------------------------------------ // Helpers @@ -66,7 +66,7 @@ static std::string latex_escape (const std::string &s) { } // ------------------------------------------------------------ -// Model +// Data model // ------------------------------------------------------------ struct Quote { @@ -97,12 +97,14 @@ struct ParseContext { } void finalize_quote() { + // Ignore incomplete entries if (cur_title.empty() || cur_meta.empty()) { cur_meta.clear(); cur_text.clear(); return; } + // Preserve insertion order of sections if (seen.find (cur_title) == seen.end()) { seen.emplace (cur_title, order.size()); order.push_back (cur_title); @@ -116,52 +118,71 @@ struct ParseContext { }; // ------------------------------------------------------------ -// Handlers +// Parsing stages // ------------------------------------------------------------ enum class Stage { Title, Meta, Body }; +// Explicit list of all supported stages for runtime validation 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; + + // Process a single line and decide the next stage + 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 { + void handle (const std::string &line, + ParseContext &ctx, + Stage &next) override { + + // Skip empty lines between entries if (line.empty()) { - next = Stage::Title; // пропускаем пустые между блоками + 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 { + 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 { + void handle (const std::string &line, + ParseContext &ctx, + Stage &next) override { + + // Separator marks the end of a clipping block if (line == "==========") { ctx.finalize_quote(); next = Stage::Title; return; } - ctx.add_text (line); // в том числе пустые строки внутри цитаты + + // Keep body lines as-is (including empty ones) + ctx.add_text (line); next = Stage::Body; } }; // ------------------------------------------------------------ -// Factory wiring (using your typefactory.h) + caching handlers +// Factory wiring + handler caching // ------------------------------------------------------------ using HandlerPtr = std::shared_ptr; @@ -169,29 +190,33 @@ using HandlerMap = std::unordered_map; static TypeFactory build_factory() { TypeFactory f; + f.registerType (Stage::Title); f.registerType (Stage::Meta); f.registerType (Stage::Body); + return f; } -static HandlerMap build_handlers_cache (const TypeFactory &factory) { +static HandlerMap build_handlers_cache ( + const TypeFactory &factory) { + HandlerMap handlers; handlers.reserve (kAllStages.size()); - // Рантайм-валидация: для каждого Stage обязаны уметь создать handler + // Runtime validation: ensure each stage has a registered handler for (Stage st : kAllStages) { try { - auto h = factory.create (st); // shared_ptr + auto h = factory.create (st); if (!h) throw std::runtime_error ("Factory returned null handler"); handlers.emplace (st, std::move (h)); } catch (const std::out_of_range &) { - throw std::runtime_error ("Missing handler registration for some Stage"); + throw std::runtime_error ("Missing handler registration for Stage"); } } - // Доп.проверка: ensure all are present (на случай коллизий/ошибок emplace) + // Extra consistency check if (handlers.size() != kAllStages.size()) throw std::runtime_error ("Handler cache size mismatch"); @@ -199,7 +224,7 @@ static HandlerMap build_handlers_cache (const TypeFactory & } // ------------------------------------------------------------ -// CLI +// CLI handling // ------------------------------------------------------------ struct CliArgs { @@ -208,10 +233,14 @@ struct CliArgs { }; static void print_usage (const char *argv0) { - std::cerr << "Usage: " << argv0 << " --input --output \n"; + std::cerr << "Usage: " << argv0 + << " --input --output \n"; } -static bool parse_args (int argc, char **argv, CliArgs &out) { +static bool parse_args (int argc, + char **argv, + CliArgs &out) { + static option long_opts[] = { {"input", required_argument, nullptr, 'i'}, {"output", required_argument, nullptr, 'o'}, @@ -220,7 +249,10 @@ static bool parse_args (int argc, char **argv, CliArgs &out) { }; int c = 0; - while ((c = ::getopt_long (argc, argv, "i:o:h", long_opts, nullptr)) != -1) { + while ((c = ::getopt_long (argc, argv, + "i:o:h", + long_opts, + nullptr)) != -1) { switch (c) { case 'i': out.input = optarg; @@ -241,29 +273,34 @@ static bool parse_args (int argc, char **argv, CliArgs &out) { print_usage (argv[0]); return false; } + return true; } // ------------------------------------------------------------ -// Convert +// Conversion logic // ------------------------------------------------------------ -static int convert (const std::string &in_path, const std::string &out_path) { +static int convert (const std::string &in_path, + const std::string &out_path) { + std::ifstream in (in_path); if (!in.is_open()) { - std::cerr << "Failed to open input file: " << in_path + std::cerr << "Failed to open input file: " + << in_path << " (" << std::strerror (errno) << ")\n"; return 2; } - // Build factory + cache handlers once + // Build factory and cache handlers once 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"; + std::cerr << "Internal error while building handler cache: " + << e.what() << "\n"; return 4; } @@ -276,7 +313,7 @@ static int convert (const std::string &in_path, const std::string &out_path) { auto it = handlers.find (stage); if (it == handlers.end() || !it->second) { - std::cerr << "Internal error: handler missing at runtime\n"; + std::cerr << "Internal error: missing handler at runtime\n"; return 4; } @@ -285,16 +322,18 @@ static int convert (const std::string &in_path, const std::string &out_path) { stage = next; } - // EOF: если файл не закончился "==========", всё равно зафиксируем последний блок + // Ensure the last entry is flushed if file does not end with separator ctx.finalize_quote(); std::ofstream out (out_path, std::ios::trunc); if (!out.is_open()) { - std::cerr << "Failed to open output file: " << out_path + std::cerr << "Failed to open output file: " + << out_path << " (" << std::strerror (errno) << ")\n"; return 3; } + // Generate LaTeX output for (std::size_t i = 0; i < ctx.order.size(); ++i) { const auto &title = ctx.order[i]; out << "\\section {" << latex_escape (title) << "}\n"; @@ -305,8 +344,10 @@ static int convert (const std::string &in_path, const std::string &out_path) { for (const auto &q : it->second) { out << " \\subsection {" << latex_escape (q.meta) << "}\n"; + for (const auto &tl : q.text_lines) out << " " << latex_escape (tl) << "\n"; + out << " \\subsubsection{notes}\n\n"; } @@ -321,5 +362,6 @@ int main (int argc, char **argv) { CliArgs args; if (!parse_args (argc, argv, args)) return 1; + return convert (args.input, args.output); }