Skip to content

Commit 39cbe5c

Browse files
committed
Comments, cleanup
1 parent d32d851 commit 39cbe5c

File tree

5 files changed

+145
-70
lines changed

5 files changed

+145
-70
lines changed

lib/util/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ target_sources(${PROJECT_NAME} PRIVATE
6666
include/${target_prefix}/util/async_queue.hpp
6767
include/${target_prefix}/util/bool.hpp
6868
include/${target_prefix}/util/byte_buffer.hpp
69-
include/${target_prefix}/util/cmd_args.hpp
69+
include/${target_prefix}/util/cli_opts.hpp
7070
include/${target_prefix}/util/colour_space.hpp
7171
include/${target_prefix}/util/data_provider.hpp
7272
include/${target_prefix}/util/enum_array.hpp
@@ -90,7 +90,7 @@ target_sources(${PROJECT_NAME} PRIVATE
9090
include/${target_prefix}/util/version.hpp
9191
include/${target_prefix}/util/visitor.hpp
9292

93-
src/cmd_args.cpp
93+
src/cli_opts.cpp
9494
src/data_provider.cpp
9595
src/env.cpp
9696
src/image.cpp
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#pragma once
2+
#include <facade/util/ptr.hpp>
3+
#include <string>
4+
#include <vector>
5+
6+
namespace facade {
7+
///
8+
/// \brief Command line options parser.
9+
///
10+
struct CliOpts {
11+
///
12+
/// \brief Result of parsing options.
13+
///
14+
enum class Result { eContinue, eExitFailure, eExitSuccess };
15+
16+
///
17+
/// \brief Specification of an option key.
18+
///
19+
struct Key {
20+
///
21+
/// \brief Long form of the key.
22+
///
23+
std::string_view full{};
24+
///
25+
/// \brief Short / character form of the key.
26+
///
27+
char single{};
28+
29+
///
30+
/// \brief Check if Key is valid.
31+
/// \returns true if either single is non-null or full is non-empty
32+
///
33+
constexpr bool valid() const { return !full.empty() || single != '\0'; }
34+
};
35+
36+
///
37+
/// \brief Value for an option.
38+
///
39+
using Value = std::string_view;
40+
41+
///
42+
/// \brief Specification for an option consumed by a custom parser.
43+
///
44+
struct Opt {
45+
///
46+
/// \brief Key specification.
47+
///
48+
Key key{};
49+
///
50+
/// \brief Value specification (printed in help).
51+
///
52+
Value value{};
53+
///
54+
/// \brief If value is non-empty, whether it is optional.
55+
///
56+
bool is_optional_value{};
57+
///
58+
/// \brief Help text for this option.
59+
///
60+
std::string_view help{};
61+
};
62+
63+
///
64+
/// \brief Interface for custom parser.
65+
///
66+
struct Parser {
67+
///
68+
/// \brief Callback for a parsed option.
69+
/// \param key Option key
70+
/// \param value Value passed (if any)
71+
///
72+
virtual void opt(Key key, Value value) = 0;
73+
};
74+
75+
///
76+
/// \brief Specification for application.
77+
///
78+
struct Spec {
79+
///
80+
/// \brief List of desired options (requires a custom parser).
81+
///
82+
/// Invalid keys are removed from the list.
83+
///
84+
std::vector<Opt> options{};
85+
///
86+
/// \brief Application version (printed in help).
87+
///
88+
std::string_view version{"(unknown)"};
89+
};
90+
91+
///
92+
/// \brief Parse help, version, and custom command line args.
93+
/// \param spec Application spec
94+
/// \param out A custom parser (used if custom options are in spec)
95+
/// \param argc Number of command line arguments
96+
/// \param argv Pointer to first argument
97+
///
98+
static Result parse(Spec spec, Ptr<Parser> out, int argc, char const* const* argv);
99+
///
100+
/// \brief Parse help and version command line args.
101+
/// \param version Application version
102+
/// \param argc Number of command line arguments
103+
/// \param argv Pointer to first argument
104+
///
105+
static Result parse(std::string_view version, int argc, char const* const* argv);
106+
};
107+
} // namespace facade

lib/util/include/facade/util/cmd_args.hpp

Lines changed: 0 additions & 38 deletions
This file was deleted.

lib/util/src/cmd_args.cpp renamed to lib/util/src/cli_opts.cpp

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#include <fmt/format.h>
2-
#include <facade/util/cmd_args.hpp>
2+
#include <facade/util/cli_opts.hpp>
33
#include <facade/util/visitor.hpp>
44
#include <algorithm>
55
#include <cassert>
@@ -12,7 +12,7 @@ namespace facade {
1212
namespace {
1313
namespace fs = std::filesystem;
1414

15-
CmdArgs::Opt const* find_opt(CmdArgs::Spec const& spec, std::variant<std::string_view, char> key) {
15+
CliOpts::Opt const* find_opt(CliOpts::Spec const& spec, std::variant<std::string_view, char> key) {
1616
for (auto const& opt : spec.options) {
1717
bool match{};
1818
auto const visitor = Visitor{
@@ -30,22 +30,25 @@ CmdArgs::Opt const* find_opt(CmdArgs::Spec const& spec, std::variant<std::string
3030
}
3131

3232
struct OptParser {
33-
using Result = CmdArgs::Result;
33+
using Result = CliOpts::Result;
3434

35-
CmdArgs::Spec spec{};
36-
Ptr<CmdArgs::Parser> out;
35+
CliOpts::Spec valid_spec{};
36+
Ptr<CliOpts::Parser> out;
3737
std::string exe_name{};
3838

39-
OptParser(CmdArgs::Spec spec, Ptr<CmdArgs::Parser> out, char const* arg0) : spec(std::move(spec)), out(out) {
40-
std::erase_if(this->spec.options, [](CmdArgs::Opt const& o) { return !o.key.valid(); });
41-
this->spec.options.push_back(CmdArgs::Opt{.key = {.full = "help"}, .help = "Show this help text"});
42-
this->spec.options.push_back(CmdArgs::Opt{.key = {.full = "version"}, .help = "Show the version"});
39+
OptParser(CliOpts::Spec spec, Ptr<CliOpts::Parser> out, char const* arg0) : out(out) {
40+
valid_spec.version = spec.version.empty() ? "(unknown)" : spec.version;
41+
std::erase_if(spec.options, [](CliOpts::Opt const& o) { return !o.key.valid(); });
42+
valid_spec.options.reserve(spec.options.size() + 2);
43+
std::move(spec.options.begin(), spec.options.end(), std::back_inserter(valid_spec.options));
44+
valid_spec.options.push_back(CliOpts::Opt{.key = {.full = "help"}, .help = "Show this help text"});
45+
valid_spec.options.push_back(CliOpts::Opt{.key = {.full = "version"}, .help = "Display the version"});
4346
exe_name = fs::path{arg0}.filename().stem().generic_string();
4447
}
4548

4649
std::size_t get_max_width() const {
4750
auto ret = std::size_t{};
48-
for (auto const& opt : spec.options) {
51+
for (auto const& opt : valid_spec.options) {
4952
auto width = opt.key.full.size();
5053
if (!opt.value.empty()) {
5154
width += 1; // =
@@ -64,7 +67,7 @@ struct OptParser {
6467
str.reserve(1024);
6568
fmt::format_to(std::back_inserter(str), "Usage: {} [OPTION]...\n\n", exe_name);
6669
auto const max_width = get_max_width();
67-
for (auto const& opt : spec.options) {
70+
for (auto const& opt : valid_spec.options) {
6871
fmt::format_to(std::back_inserter(str), " {}{}", (opt.key.single ? '-' : ' '), (opt.key.single ? opt.key.single : ' '));
6972
if (!opt.key.full.empty()) { fmt::format_to(std::back_inserter(str), "{} --{}", (opt.key.single ? ',' : ' '), opt.key.full); }
7073
auto width = opt.key.full.size();
@@ -82,11 +85,11 @@ struct OptParser {
8285
}
8386

8487
Result print_version() const {
85-
std::cout << fmt::format("{} version {}\n", exe_name, spec.version);
88+
std::cout << fmt::format("{} version {}\n", exe_name, valid_spec.version);
8689
return Result::eExitSuccess;
8790
}
8891

89-
CmdArgs::Result opt(CmdArgs::Key key, CmdArgs::Value value) const {
92+
CliOpts::Result opt(CliOpts::Key key, CliOpts::Value value) const {
9093
if (key.full == "help") { return print_help(); }
9194
if (key.full == "version") { return print_version(); }
9295
if (out) { out->opt(key, value); }
@@ -95,7 +98,7 @@ struct OptParser {
9598
};
9699

97100
struct ParseOpt {
98-
using Result = CmdArgs::Result;
101+
using Result = CliOpts::Result;
99102

100103
OptParser const& out;
101104

@@ -117,7 +120,7 @@ struct ParseOpt {
117120
return Result::eExitFailure;
118121
}
119122

120-
Result missing_value(CmdArgs::Opt const& opt) const {
123+
Result missing_value(CliOpts::Opt const& opt) const {
121124
auto str = opt.key.full;
122125
if (str.empty()) { str = {&opt.key.single, 1}; }
123126
std::cerr << fmt::format("missing required value for option: {}{}\n", (opt.key.full.empty() ? "-" : "--"), str);
@@ -126,15 +129,15 @@ struct ParseOpt {
126129

127130
Result parse_word() {
128131
auto const it = current.find('=');
129-
CmdArgs::Opt const* opt{};
130-
auto value = CmdArgs::Value{};
132+
CliOpts::Opt const* opt{};
133+
auto value = CliOpts::Value{};
131134
if (it != std::string_view::npos) {
132135
auto const key = current.substr(0, it);
133-
opt = find_opt(out.spec, key);
136+
opt = find_opt(out.valid_spec, key);
134137
if (!opt) { return unknown_option(key); }
135138
value = current.substr(it + 1);
136139
} else {
137-
opt = find_opt(out.spec, current);
140+
opt = find_opt(out.valid_spec, current);
138141
if (!opt) { return unknown_option(current); }
139142
}
140143
if (!opt->value.empty() && !opt->is_optional_value && value.empty()) { return missing_value(*opt); }
@@ -145,14 +148,14 @@ struct ParseOpt {
145148
char prev{};
146149
while (!at_end() && peek() != '=') {
147150
prev = advance();
148-
auto const* opt = find_opt(out.spec, prev);
151+
auto const* opt = find_opt(out.valid_spec, prev);
149152
if (!opt) { return unknown_option({&prev, 1}); }
150153
return out.opt(opt->key, {});
151154
}
152155
if (peek() == '=') {
153156
if (prev == '\0') { return unknown_option(""); }
154157
advance();
155-
auto const* opt = find_opt(out.spec, prev);
158+
auto const* opt = find_opt(out.valid_spec, prev);
156159
if (!opt) { return unknown_option({&prev, 1}); }
157160
return out.opt(opt->key, current);
158161
}
@@ -176,7 +179,7 @@ struct ParseOpt {
176179

177180
} // namespace
178181

179-
auto CmdArgs::parse(Spec spec, Ptr<Parser> out, int argc, char const* const* argv) -> Result {
182+
auto CliOpts::parse(Spec spec, Ptr<Parser> out, int argc, char const* const* argv) -> Result {
180183
if (argc < 1) { return Result::eContinue; }
181184
auto opt_parser = OptParser{std::move(spec), out, argv[0]};
182185
auto parse_opt = ParseOpt{opt_parser};
@@ -186,7 +189,7 @@ auto CmdArgs::parse(Spec spec, Ptr<Parser> out, int argc, char const* const* arg
186189
return Result::eContinue;
187190
}
188191

189-
auto CmdArgs::parse(std::string_view version, int argc, char const* const* argv) -> Result {
192+
auto CliOpts::parse(std::string_view version, int argc, char const* const* argv) -> Result {
190193
auto spec = Spec{.version = version};
191194
return parse(std::move(spec), {}, argc, argv);
192195
}

src/main.cpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,16 @@ using namespace facade;
2828
namespace {
2929
namespace fs = std::filesystem;
3030

31+
std::string_view version_string() {
32+
static auto const ret = fmt::format("v{}.{}.{}", version_v.major, version_v.minor, version_v.patch);
33+
return ret;
34+
}
35+
3136
void log_prologue() {
3237
auto const now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
3338
char buf[32]{};
3439
std::strftime(buf, sizeof(buf), "%F %Z", std::localtime(&now));
35-
static constexpr auto v = version_v;
36-
logger::info("facade v{}.{}.{} | {} |", v.major, v.minor, v.patch, buf);
40+
logger::info("facade {} | {} |", version_string(), buf);
3741
}
3842

3943
void run() {
@@ -178,11 +182,10 @@ void run() {
178182
int main(int argc, char** argv) {
179183
try {
180184
auto logger_instance = logger::Instance{};
181-
auto const version_str = fmt::format("v{}.{}.{}", version_v.major, version_v.minor, version_v.patch);
182-
switch (CmdArgs::parse(version_str, argc, argv)) {
183-
case CmdArgs::Result::eExitSuccess: return EXIT_SUCCESS;
184-
case CmdArgs::Result::eExitFailure: return EXIT_FAILURE;
185-
case CmdArgs::Result::eContinue: break;
185+
switch (CliOpts::parse(version_string(), argc, argv)) {
186+
case CliOpts::Result::eExitSuccess: return EXIT_SUCCESS;
187+
case CliOpts::Result::eExitFailure: return EXIT_FAILURE;
188+
case CliOpts::Result::eContinue: break;
186189
}
187190
try {
188191
run();

0 commit comments

Comments
 (0)