Skip to content

Commit e8fd4f7

Browse files
committed
[WIP] Start refactoring and simplifying CLI boilerplate code
Signed-off-by: Juan Cruz Viotti <[email protected]>
1 parent 9759a4c commit e8fd4f7

10 files changed

+74
-85
lines changed

src/command_bundle.cc

+16-23
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#include <sourcemeta/core/json.h>
22
#include <sourcemeta/core/jsonschema.h>
33

4-
#include <cstdlib> // EXIT_SUCCESS
4+
#include <cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
55
#include <iostream> // std::cout
66

77
#include "command.h"
@@ -11,37 +11,30 @@ auto sourcemeta::jsonschema::cli::bundle(
1111
const std::span<const std::string> &arguments) -> int {
1212
const auto options{
1313
parse_options(arguments, {"h", "http", "w", "without-id"})};
14-
const auto dialect{default_dialect(options)};
15-
1614
if (options.at("").size() < 1) {
17-
std::cerr
18-
<< "error: This command expects a path to a schema. For example:\n\n"
19-
<< " jsonschema bundle path/to/schema.json\n";
15+
log_error() << "This command expects a path to a schema. For example:\n\n"
16+
<< " jsonschema bundle path/to/schema.json\n";
2017
return EXIT_FAILURE;
2118
}
2219

23-
const auto custom_resolver{resolver(
24-
options, options.contains("h") || options.contains("http"), dialect)};
25-
auto schema{sourcemeta::jsonschema::cli::read_file(options.at("").front())};
26-
20+
const auto default_dialect{infer_default_dialect(options)};
21+
const auto resolver{infer_resolver(options, default_dialect)};
22+
auto schema{read_yaml_or_json(options.at("").front())};
2723
sourcemeta::core::bundle(schema, sourcemeta::core::schema_official_walker,
28-
custom_resolver, dialect);
24+
resolver, default_dialect);
2925

3026
if (options.contains("w") || options.contains("without-id")) {
31-
std::cerr << "warning: You are opting in to remove schema identifiers in "
32-
"the bundled schema.\n";
33-
std::cerr << "The only legit use case of this advanced feature we know of "
34-
"it to workaround\n";
35-
std::cerr << "non-compliant JSON Schema implementations such as Visual "
36-
"Studio Code.\n";
37-
std::cerr << "In other case, this is not needed and may harm other use "
38-
"cases. For example,\n";
39-
std::cerr << "you will be unable to reference the resulting schema from "
40-
"other schemas\n";
41-
std::cerr << "using the --resolve/-r option.\n";
27+
log_warning()
28+
<< "You are opting in to remove schema identifiers in "
29+
"the bundled schema.\nThe only legit use case of this "
30+
"advanced feature we know of it to workaround\nnon-compliant "
31+
"JSON Schema implementations such as Visual Studio Code.\nIn "
32+
"other case, this is not needed and may harm other use cases. "
33+
"For example,\nyou will be unable to reference the resulting "
34+
"schema from other schemas\nusing the --resolve/-r option.\n";
4235
sourcemeta::core::unidentify(schema,
4336
sourcemeta::core::schema_official_walker,
44-
custom_resolver, dialect);
37+
resolver, default_dialect);
4538
}
4639

4740
sourcemeta::core::prettify(schema, std::cout,

src/command_decode.cc

+5-6
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ static auto has_data(std::ifstream &stream) -> bool {
2727

2828
auto sourcemeta::jsonschema::cli::decode(
2929
const std::span<const std::string> &arguments) -> int {
30-
const auto options{parse_options(arguments, {})};
30+
const auto options{parse_options(arguments, {"h", "http"})};
3131

3232
if (options.at("").size() < 2) {
3333
std::cerr
@@ -42,11 +42,10 @@ auto sourcemeta::jsonschema::cli::decode(
4242
"$schema": "https://json-schema.org/draft/2020-12/schema"
4343
})JSON")};
4444

45-
const auto dialect{default_dialect(options)};
46-
sourcemeta::jsonbinpack::compile(
47-
schema, sourcemeta::core::schema_official_walker,
48-
resolver(options, options.contains("h") || options.contains("http"),
49-
dialect));
45+
const auto default_dialect{infer_default_dialect(options)};
46+
sourcemeta::jsonbinpack::compile(schema,
47+
sourcemeta::core::schema_official_walker,
48+
infer_resolver(options, default_dialect));
5049
const auto encoding{sourcemeta::jsonbinpack::load(schema)};
5150

5251
std::ifstream input_stream{sourcemeta::jsonschema::cli::safe_weakly_canonical(

src/command_encode.cc

+6-8
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
auto sourcemeta::jsonschema::cli::encode(
1616
const std::span<const std::string> &arguments) -> int {
17-
const auto options{parse_options(arguments, {})};
17+
const auto options{parse_options(arguments, {"h", "http"})};
1818

1919
if (options.at("").size() < 2) {
2020
std::cerr
@@ -29,11 +29,10 @@ auto sourcemeta::jsonschema::cli::encode(
2929
"$schema": "https://json-schema.org/draft/2020-12/schema"
3030
})JSON")};
3131

32-
const auto dialect{default_dialect(options)};
33-
sourcemeta::jsonbinpack::compile(
34-
schema, sourcemeta::core::schema_official_walker,
35-
resolver(options, options.contains("h") || options.contains("http"),
36-
dialect));
32+
const auto default_dialect{infer_default_dialect(options)};
33+
sourcemeta::jsonbinpack::compile(schema,
34+
sourcemeta::core::schema_official_walker,
35+
infer_resolver(options, default_dialect));
3736
const auto encoding{sourcemeta::jsonbinpack::load(schema)};
3837

3938
const std::filesystem::path document{options.at("").front()};
@@ -64,8 +63,7 @@ auto sourcemeta::jsonschema::cli::encode(
6463
<< (static_cast<std::uint64_t>(total_size) * 100 / original_size)
6564
<< "%\n";
6665
} else {
67-
const auto entry{
68-
sourcemeta::jsonschema::cli::read_file(options.at("").front())};
66+
const auto entry{read_yaml_or_json(options.at("").front())};
6967
std::ofstream output_stream(safe_weakly_canonical(options.at("").at(1)),
7068
std::ios::binary);
7169
output_stream.exceptions(std::ios_base::badbit);

src/command_inspect.cc

+4-7
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
auto sourcemeta::jsonschema::cli::inspect(
1111
const std::span<const std::string> &arguments) -> int {
12-
const auto options{parse_options(arguments, {})};
12+
const auto options{parse_options(arguments, {"h", "http"})};
1313
if (options.at("").size() < 1) {
1414
std::cerr
1515
<< "error: This command expects a path to a schema. For example:\n\n"
@@ -18,17 +18,14 @@ auto sourcemeta::jsonschema::cli::inspect(
1818
}
1919

2020
const sourcemeta::core::JSON schema{
21-
sourcemeta::jsonschema::cli::read_file(options.at("").front())};
21+
read_yaml_or_json(options.at("").front())};
2222

2323
sourcemeta::core::SchemaFrame frame{
2424
sourcemeta::core::SchemaFrame::Mode::Instances};
2525

26-
const auto dialect{default_dialect(options)};
26+
const auto dialect{infer_default_dialect(options)};
2727
frame.analyse(schema, sourcemeta::core::schema_official_walker,
28-
resolver(options,
29-
options.contains("h") || options.contains("http"),
30-
dialect),
31-
dialect);
28+
infer_resolver(options, dialect), dialect);
3229

3330
if (options.contains("json") || options.contains("j")) {
3431
sourcemeta::core::prettify(frame.to_json(), std::cout);

src/command_lint.cc

+4-8
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ static auto disable_lint_rules(sourcemeta::core::SchemaTransformer &bundle,
3131
auto sourcemeta::jsonschema::cli::lint(
3232
const std::span<const std::string> &arguments) -> int {
3333
const auto options{parse_options(
34-
arguments, {"f", "fix", "json", "j", "k", "keep-ordering"})};
34+
arguments, {"f", "fix", "json", "j", "k", "keep-ordering", "h", "http"})};
3535
const bool output_json = options.contains("json") || options.contains("j");
3636

3737
sourcemeta::core::SchemaTransformer bundle;
@@ -63,7 +63,7 @@ auto sourcemeta::jsonschema::cli::lint(
6363

6464
bool result{true};
6565
auto errors_array = sourcemeta::core::JSON::make_array();
66-
const auto dialect{default_dialect(options)};
66+
const auto dialect{infer_default_dialect(options)};
6767

6868
if (options.contains("f") || options.contains("fix")) {
6969
for (const auto &entry :
@@ -78,10 +78,7 @@ auto sourcemeta::jsonschema::cli::lint(
7878

7979
auto copy = entry.second;
8080
bundle.apply(copy, sourcemeta::core::schema_official_walker,
81-
resolver(options,
82-
options.contains("h") || options.contains("http"),
83-
dialect),
84-
dialect);
81+
infer_resolver(options, dialect), dialect);
8582
std::ofstream output{entry.first};
8683
if (options.contains("k") || options.contains("keep-ordering")) {
8784
sourcemeta::core::prettify(copy, output);
@@ -97,8 +94,7 @@ auto sourcemeta::jsonschema::cli::lint(
9794
log_verbose(options) << "Linting: " << entry.first.string() << "\n";
9895
const bool subresult = bundle.check(
9996
entry.second, sourcemeta::core::schema_official_walker,
100-
resolver(options, options.contains("h") || options.contains("http"),
101-
dialect),
97+
infer_resolver(options, dialect),
10298
[&](const auto &pointer, const auto &name, const auto &message) {
10399
if (output_json) {
104100
auto error_obj = sourcemeta::core::JSON::make_object();

src/command_metaschema.cc

+2-4
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@ auto sourcemeta::jsonschema::cli::metaschema(
1818
const std::span<const std::string> &arguments) -> int {
1919
const auto options{parse_options(arguments, {"h", "http", "t", "trace"})};
2020
const auto trace{options.contains("t") || options.contains("trace")};
21-
const auto default_dialect_option{default_dialect(options)};
22-
const auto custom_resolver{
23-
resolver(options, options.contains("h") || options.contains("http"),
24-
default_dialect_option)};
21+
const auto default_dialect_option{infer_default_dialect(options)};
22+
const auto custom_resolver{infer_resolver(options, default_dialect_option)};
2523
bool result{true};
2624
sourcemeta::blaze::Evaluator evaluator;
2725

src/command_test.cc

+4-5
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ static auto get_data(const sourcemeta::core::JSON &test_case,
3333
}
3434

3535
try {
36-
return sourcemeta::jsonschema::cli::read_file(data_path);
36+
return sourcemeta::jsonschema::cli::read_yaml_or_json(data_path);
3737
} catch (...) {
3838
std::cout << "\n";
3939
throw;
@@ -44,16 +44,15 @@ auto sourcemeta::jsonschema::cli::test(
4444
const std::span<const std::string> &arguments) -> int {
4545
const auto options{parse_options(arguments, {"h", "http"})};
4646
bool result{true};
47-
const auto dialect{default_dialect(options)};
48-
const auto test_resolver{resolver(
49-
options, options.contains("h") || options.contains("http"), dialect)};
47+
const auto dialect{infer_default_dialect(options)};
48+
const auto test_resolver{infer_resolver(options, dialect)};
5049
const auto verbose{options.contains("verbose") || options.contains("v")};
5150
sourcemeta::blaze::Evaluator evaluator;
5251

5352
for (const auto &entry : for_each_json(options.at(""), parse_ignore(options),
5453
parse_extensions(options))) {
5554
const sourcemeta::core::JSON test{
56-
sourcemeta::jsonschema::cli::read_file(entry.first)};
55+
sourcemeta::jsonschema::cli::read_yaml_or_json(entry.first)};
5756
std::cout << entry.first.string() << ":";
5857

5958
if (!test.is_object()) {

src/command_validate.cc

+4-7
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
#include "utils.h"
1616

1717
// TODO: Add a flag to emit output using the standard JSON Schema output format
18-
// TODO: Add a flag to collect annotations
1918
// TODO: Add a flag to take a pre-compiled schema as input
2019
auto sourcemeta::jsonschema::cli::validate(
2120
const std::span<const std::string> &arguments) -> int {
@@ -39,11 +38,10 @@ auto sourcemeta::jsonschema::cli::validate(
3938
}
4039

4140
const auto &schema_path{options.at("").at(0)};
42-
const auto dialect{default_dialect(options)};
43-
const auto custom_resolver{resolver(
44-
options, options.contains("h") || options.contains("http"), dialect)};
41+
const auto dialect{infer_default_dialect(options)};
42+
const auto custom_resolver{infer_resolver(options, dialect)};
4543

46-
const auto schema{sourcemeta::jsonschema::cli::read_file(schema_path)};
44+
const auto schema{read_yaml_or_json(schema_path)};
4745

4846
if (!sourcemeta::core::is_schema(schema)) {
4947
std::cerr << "error: The schema file you provided does not represent a "
@@ -138,8 +136,7 @@ auto sourcemeta::jsonschema::cli::validate(
138136
log_verbose(options) << "warning: The JSONL file is empty\n";
139137
}
140138
} else {
141-
const auto instance{
142-
sourcemeta::jsonschema::cli::read_file(instance_path)};
139+
const auto instance{read_yaml_or_json(instance_path)};
143140
std::ostringstream error;
144141
sourcemeta::blaze::SimpleOutput output{instance};
145142
sourcemeta::blaze::TraceOutput trace_output;

src/utils.cc

+16-9
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@ auto handle_json_entry(
6161
}
6262

6363
// TODO: Print a verbose message for what is getting parsed
64-
result.emplace_back(canonical,
65-
sourcemeta::jsonschema::cli::read_file(canonical));
64+
result.emplace_back(
65+
canonical,
66+
sourcemeta::jsonschema::cli::read_yaml_or_json(canonical));
6667
}
6768
}
6869
} else {
@@ -88,8 +89,8 @@ auto handle_json_entry(
8889
}
8990

9091
// TODO: Print a verbose message for what is getting parsed
91-
result.emplace_back(canonical,
92-
sourcemeta::jsonschema::cli::read_file(canonical));
92+
result.emplace_back(
93+
canonical, sourcemeta::jsonschema::cli::read_yaml_or_json(canonical));
9394
}
9495
}
9596
}
@@ -108,7 +109,8 @@ auto normalize_extension(const std::string &extension) -> std::string {
108109

109110
namespace sourcemeta::jsonschema::cli {
110111

111-
auto read_file(const std::filesystem::path &path) -> sourcemeta::core::JSON {
112+
auto read_yaml_or_json(const std::filesystem::path &path)
113+
-> sourcemeta::core::JSON {
112114
if (path.extension() == ".yaml" || path.extension() == ".yml") {
113115
return sourcemeta::core::read_yaml(path);
114116
}
@@ -309,10 +311,11 @@ static auto fallback_resolver(
309311
}
310312
}
311313

312-
auto resolver(const std::map<std::string, std::vector<std::string>> &options,
313-
const bool remote,
314-
const std::optional<std::string> &default_dialect)
314+
auto infer_resolver(
315+
const std::map<std::string, std::vector<std::string>> &options,
316+
const std::optional<std::string> &default_dialect)
315317
-> sourcemeta::core::SchemaResolver {
318+
const auto remote{options.contains("h") || options.contains("http")};
316319
sourcemeta::core::SchemaMapResolver dynamic_resolver{
317320
[remote, &options](std::string_view identifier) {
318321
if (remote) {
@@ -367,6 +370,10 @@ auto log_verbose(const std::map<std::string, std::vector<std::string>> &options)
367370
return null_stream;
368371
}
369372

373+
auto log_error() -> std::ostream & { return std::cerr << "error: "; }
374+
375+
auto log_warning() -> std::ostream & { return std::cerr << "warning: "; }
376+
370377
auto parse_extensions(
371378
const std::map<std::string, std::vector<std::string>> &options)
372379
-> std::set<std::string> {
@@ -426,7 +433,7 @@ auto safe_weakly_canonical(const std::filesystem::path &input)
426433
: std::filesystem::weakly_canonical(input);
427434
}
428435

429-
auto default_dialect(
436+
auto infer_default_dialect(
430437
const std::map<std::string, std::vector<std::string>> &options)
431438
-> std::optional<std::string> {
432439

src/utils.h

+13-8
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919

2020
namespace sourcemeta::jsonschema::cli {
2121

22-
auto read_file(const std::filesystem::path &path) -> sourcemeta::core::JSON;
23-
2422
auto parse_options(const std::span<const std::string> &arguments,
2523
const std::set<std::string> &flags)
2624
-> std::map<std::string, std::vector<std::string>>;
@@ -41,14 +39,13 @@ auto print_annotations(
4139
auto print(const sourcemeta::blaze::TraceOutput &output, std::ostream &stream)
4240
-> void;
4341

44-
auto resolver(const std::map<std::string, std::vector<std::string>> &options,
45-
const bool remote,
46-
const std::optional<std::string> &default_dialect)
47-
-> sourcemeta::core::SchemaResolver;
48-
4942
auto log_verbose(const std::map<std::string, std::vector<std::string>> &options)
5043
-> std::ostream &;
5144

45+
auto log_error() -> std::ostream &;
46+
47+
auto log_warning() -> std::ostream &;
48+
5249
auto parse_extensions(
5350
const std::map<std::string, std::vector<std::string>> &options)
5451
-> std::set<std::string>;
@@ -60,7 +57,15 @@ auto parse_ignore(
6057
auto safe_weakly_canonical(const std::filesystem::path &input)
6158
-> std::filesystem::path;
6259

63-
auto default_dialect(
60+
auto read_yaml_or_json(const std::filesystem::path &path)
61+
-> sourcemeta::core::JSON;
62+
63+
auto infer_resolver(
64+
const std::map<std::string, std::vector<std::string>> &options,
65+
const std::optional<std::string> &default_dialect)
66+
-> sourcemeta::core::SchemaResolver;
67+
68+
auto infer_default_dialect(
6469
const std::map<std::string, std::vector<std::string>> &options)
6570
-> std::optional<std::string>;
6671

0 commit comments

Comments
 (0)