/* ptrnt - print labels on linux Copyright (C) 2025 Moritz Martinius This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "CliParser.hpp" #include #include namespace ptprnt::cli { CliParser::CliParser(std::string appDescription, std::string versionString) : mApp(std::move(appDescription)), mVersionString(std::move(versionString)) { setupParser(); } int CliParser::parse(int argc, char** argv) { try { mApp.parse(argc, argv); } catch (const CLI::CallForHelp& e) { // User requested help - display it and signal clean exit mApp.exit(e); return 1; // Signal: exit cleanly } catch (const CLI::CallForVersion&) { // User requested version - already displayed by callback return 1; // Signal: exit cleanly } catch (const CLI::ParseError& e) { // Parse error - display error message mApp.exit(e); return -1; // Signal: error } // Post-process: Re-order commands based on actual command line order // This is needed because CLI11 groups options by type reorderCommandsByArgv(argc, argv); return 0; } void CliParser::reorderCommandsByArgv(int argc, char** argv) { std::vector reorderedCommands; // Parse argv to determine the actual order for (int i = 1; i < argc; ++i) { std::string arg = argv[i]; if (arg == "--new") { reorderedCommands.emplace_back(CommandType::NewLabel, ""); } else if (arg == "-t" || arg == "--text") { if (i + 1 < argc) { reorderedCommands.emplace_back(CommandType::Text, argv[++i]); } } else if (arg == "-f" || arg == "--font") { if (i + 1 < argc) { reorderedCommands.emplace_back(CommandType::Font, argv[++i]); } } else if (arg == "-s" || arg == "--fontsize") { if (i + 1 < argc) { reorderedCommands.emplace_back(CommandType::FontSize, argv[++i]); } } else if (arg == "--valign") { if (i + 1 < argc) { reorderedCommands.emplace_back(CommandType::VAlign, argv[++i]); } } else if (arg == "--halign") { if (i + 1 < argc) { reorderedCommands.emplace_back(CommandType::HAlign, argv[++i]); } } } // Only replace if we found relevant commands if (!reorderedCommands.empty()) { mOptions.commands = std::move(reorderedCommands); } } void CliParser::setupParser() { // Version callback auto printVersion = [this](std::size_t) { std::cout << std::format("ptprnt version: {}\n", mVersionString); throw CLI::CallForVersion(); }; // General options mApp.add_flag("-v,--verbose", mOptions.verbose, "Enable verbose output"); mApp.add_flag("--trace", mOptions.trace, "Enable trace output (shows USB communication)"); mApp.add_flag("-V,--version", printVersion, "Prints the ptprnt's version"); // Printer selection mApp.add_option("-p,--printer", mOptions.printerSelection, "Select printer driver (default: auto). Use --list-all-drivers to see available options") ->default_val("auto"); mApp.add_flag("--list-all-drivers", mOptions.listDrivers, "List all available printer drivers and exit"); // Text printing options // Note: CLI11 options are processed in order when using ->each() with callbacks mApp.add_option( "-t,--text", "Text to print (can be used multiple times, use formatting options before to influence text layout)") ->multi_option_policy(CLI::MultiOptionPolicy::TakeAll) ->each([this](const std::string& text) { mOptions.commands.emplace_back(CommandType::Text, text); }); // Text formatting options mApp.add_option("-f,--font", "Font used for the following text occurrences") ->multi_option_policy(CLI::MultiOptionPolicy::TakeAll) ->each([this](const std::string& font) { mOptions.commands.emplace_back(CommandType::Font, font); }); mApp.add_option("-s,--fontsize", "Font size of the following text occurrences") ->multi_option_policy(CLI::MultiOptionPolicy::TakeAll) ->each([this](const std::string& size) { mOptions.commands.emplace_back(CommandType::FontSize, size); }); mApp.add_option("--valign", "Vertical alignment of the following text occurrences") ->multi_option_policy(CLI::MultiOptionPolicy::TakeAll) ->each([this](const std::string& align) { mOptions.commands.emplace_back(CommandType::VAlign, align); }); mApp.add_option("--halign", "Horizontal alignment of the following text occurrences") ->multi_option_policy(CLI::MultiOptionPolicy::TakeAll) ->each([this](const std::string& align) { mOptions.commands.emplace_back(CommandType::HAlign, align); }); // Label separator - use an option with multi_option_policy to maintain parse order // We need to use a dummy string parameter since .each() expects a string callback mApp.add_flag("--new", "Start a new label (multiple labels will be stitched together)") ->multi_option_policy(CLI::MultiOptionPolicy::TakeAll) ->each([this](const std::string&) { mOptions.commands.emplace_back(CommandType::NewLabel, ""); }); } } // namespace ptprnt::cli