Skip to content

[WIP] Start refactoring and simplifying CLI boilerplate code #312

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 16 additions & 23 deletions src/command_bundle.cc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include <sourcemeta/core/json.h>
#include <sourcemeta/core/jsonschema.h>

#include <cstdlib> // EXIT_SUCCESS
#include <cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
#include <iostream> // std::cout

#include "command.h"
Expand All @@ -11,37 +11,30 @@ auto sourcemeta::jsonschema::cli::bundle(
const std::span<const std::string> &arguments) -> int {
const auto options{
parse_options(arguments, {"h", "http", "w", "without-id"})};
const auto dialect{default_dialect(options)};

if (options.at("").size() < 1) {
std::cerr
<< "error: This command expects a path to a schema. For example:\n\n"
<< " jsonschema bundle path/to/schema.json\n";
log_error() << "This command expects a path to a schema. For example:\n\n"
<< " jsonschema bundle path/to/schema.json\n";
return EXIT_FAILURE;
}

const auto custom_resolver{resolver(
options, options.contains("h") || options.contains("http"), dialect)};
auto schema{sourcemeta::jsonschema::cli::read_file(options.at("").front())};

const auto default_dialect{infer_default_dialect(options)};
const auto resolver{infer_resolver(options, default_dialect)};
auto schema{read_yaml_or_json(options.at("").front())};
sourcemeta::core::bundle(schema, sourcemeta::core::schema_official_walker,
custom_resolver, dialect);
resolver, default_dialect);

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

sourcemeta::core::prettify(schema, std::cout,
Expand Down
11 changes: 5 additions & 6 deletions src/command_decode.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ static auto has_data(std::ifstream &stream) -> bool {

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

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

const auto dialect{default_dialect(options)};
sourcemeta::jsonbinpack::compile(
schema, sourcemeta::core::schema_official_walker,
resolver(options, options.contains("h") || options.contains("http"),
dialect));
const auto default_dialect{infer_default_dialect(options)};
sourcemeta::jsonbinpack::compile(schema,
sourcemeta::core::schema_official_walker,
infer_resolver(options, default_dialect));
const auto encoding{sourcemeta::jsonbinpack::load(schema)};

std::ifstream input_stream{sourcemeta::jsonschema::cli::safe_weakly_canonical(
Expand Down
14 changes: 6 additions & 8 deletions src/command_encode.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

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

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

const auto dialect{default_dialect(options)};
sourcemeta::jsonbinpack::compile(
schema, sourcemeta::core::schema_official_walker,
resolver(options, options.contains("h") || options.contains("http"),
dialect));
const auto default_dialect{infer_default_dialect(options)};
sourcemeta::jsonbinpack::compile(schema,
sourcemeta::core::schema_official_walker,
infer_resolver(options, default_dialect));
const auto encoding{sourcemeta::jsonbinpack::load(schema)};

const std::filesystem::path document{options.at("").front()};
Expand Down Expand Up @@ -64,8 +63,7 @@ auto sourcemeta::jsonschema::cli::encode(
<< (static_cast<std::uint64_t>(total_size) * 100 / original_size)
<< "%\n";
} else {
const auto entry{
sourcemeta::jsonschema::cli::read_file(options.at("").front())};
const auto entry{read_yaml_or_json(options.at("").front())};
std::ofstream output_stream(safe_weakly_canonical(options.at("").at(1)),
std::ios::binary);
output_stream.exceptions(std::ios_base::badbit);
Expand Down
8 changes: 3 additions & 5 deletions src/command_fmt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@ auto sourcemeta::jsonschema::cli::fmt(
const auto options{
parse_options(arguments, {"c", "check", "k", "keep-ordering"})};

for (const auto &entry : for_each_json(options.at(""), parse_ignore(options),
parse_extensions(options))) {
if (entry.first.extension() == ".yaml" ||
entry.first.extension() == ".yml") {
std::cerr << "This command does not support YAML input files yet\n";
for (const auto &entry : for_each_json_or_yaml(options.at(""), options)) {
if (looks_like_yaml(entry.first)) {
log_error() << "This command does not support YAML input files yet\n";
return EXIT_FAILURE;
}

Expand Down
11 changes: 4 additions & 7 deletions src/command_inspect.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

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

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

sourcemeta::core::SchemaFrame frame{
sourcemeta::core::SchemaFrame::Mode::Instances};

const auto dialect{default_dialect(options)};
const auto dialect{infer_default_dialect(options)};
frame.analyse(schema, sourcemeta::core::schema_official_walker,
resolver(options,
options.contains("h") || options.contains("http"),
dialect),
dialect);
infer_resolver(options, dialect), dialect);

if (options.contains("json") || options.contains("j")) {
sourcemeta::core::prettify(frame.to_json(), std::cout);
Expand Down
19 changes: 6 additions & 13 deletions src/command_lint.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ static auto disable_lint_rules(sourcemeta::core::SchemaTransformer &bundle,
auto sourcemeta::jsonschema::cli::lint(
const std::span<const std::string> &arguments) -> int {
const auto options{parse_options(
arguments, {"f", "fix", "json", "j", "k", "keep-ordering"})};
arguments, {"f", "fix", "json", "j", "k", "keep-ordering", "h", "http"})};
const bool output_json = options.contains("json") || options.contains("j");

sourcemeta::core::SchemaTransformer bundle;
Expand Down Expand Up @@ -63,12 +63,10 @@ auto sourcemeta::jsonschema::cli::lint(

bool result{true};
auto errors_array = sourcemeta::core::JSON::make_array();
const auto dialect{default_dialect(options)};
const auto dialect{infer_default_dialect(options)};

if (options.contains("f") || options.contains("fix")) {
for (const auto &entry :
for_each_json(options.at(""), parse_ignore(options),
parse_extensions(options))) {
for (const auto &entry : for_each_json_or_yaml(options.at(""), options)) {
log_verbose(options) << "Linting: " << entry.first.string() << "\n";
if (entry.first.extension() == ".yaml" ||
entry.first.extension() == ".yml") {
Expand All @@ -78,10 +76,7 @@ auto sourcemeta::jsonschema::cli::lint(

auto copy = entry.second;
bundle.apply(copy, sourcemeta::core::schema_official_walker,
resolver(options,
options.contains("h") || options.contains("http"),
dialect),
dialect);
infer_resolver(options, dialect), dialect);
std::ofstream output{entry.first};
if (options.contains("k") || options.contains("keep-ordering")) {
sourcemeta::core::prettify(copy, output);
Expand All @@ -92,13 +87,11 @@ auto sourcemeta::jsonschema::cli::lint(
output << "\n";
}
}
for (const auto &entry : for_each_json(options.at(""), parse_ignore(options),
parse_extensions(options))) {
for (const auto &entry : for_each_json_or_yaml(options.at(""), options)) {
log_verbose(options) << "Linting: " << entry.first.string() << "\n";
const bool subresult = bundle.check(
entry.second, sourcemeta::core::schema_official_walker,
resolver(options, options.contains("h") || options.contains("http"),
dialect),
infer_resolver(options, dialect),
[&](const auto &pointer, const auto &name, const auto &message) {
if (output_json) {
auto error_obj = sourcemeta::core::JSON::make_object();
Expand Down
9 changes: 3 additions & 6 deletions src/command_metaschema.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,14 @@ auto sourcemeta::jsonschema::cli::metaschema(
const std::span<const std::string> &arguments) -> int {
const auto options{parse_options(arguments, {"h", "http", "t", "trace"})};
const auto trace{options.contains("t") || options.contains("trace")};
const auto default_dialect_option{default_dialect(options)};
const auto custom_resolver{
resolver(options, options.contains("h") || options.contains("http"),
default_dialect_option)};
const auto default_dialect_option{infer_default_dialect(options)};
const auto custom_resolver{infer_resolver(options, default_dialect_option)};
bool result{true};
sourcemeta::blaze::Evaluator evaluator;

std::map<std::string, sourcemeta::blaze::Template> cache;

for (const auto &entry : for_each_json(options.at(""), parse_ignore(options),
parse_extensions(options))) {
for (const auto &entry : for_each_json_or_yaml(options.at(""), options)) {
if (!sourcemeta::core::is_schema(entry.second)) {
std::cerr << "error: The schema file you provided does not represent a "
"valid JSON Schema\n "
Expand Down
12 changes: 5 additions & 7 deletions src/command_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ static auto get_data(const sourcemeta::core::JSON &test_case,
}

try {
return sourcemeta::jsonschema::cli::read_file(data_path);
return sourcemeta::jsonschema::cli::read_yaml_or_json(data_path);
} catch (...) {
std::cout << "\n";
throw;
Expand All @@ -44,16 +44,14 @@ auto sourcemeta::jsonschema::cli::test(
const std::span<const std::string> &arguments) -> int {
const auto options{parse_options(arguments, {"h", "http"})};
bool result{true};
const auto dialect{default_dialect(options)};
const auto test_resolver{resolver(
options, options.contains("h") || options.contains("http"), dialect)};
const auto dialect{infer_default_dialect(options)};
const auto test_resolver{infer_resolver(options, dialect)};
const auto verbose{options.contains("verbose") || options.contains("v")};
sourcemeta::blaze::Evaluator evaluator;

for (const auto &entry : for_each_json(options.at(""), parse_ignore(options),
parse_extensions(options))) {
for (const auto &entry : for_each_json_or_yaml(options.at(""), options)) {
const sourcemeta::core::JSON test{
sourcemeta::jsonschema::cli::read_file(entry.first)};
sourcemeta::jsonschema::cli::read_yaml_or_json(entry.first)};
std::cout << entry.first.string() << ":";

if (!test.is_object()) {
Expand Down
11 changes: 4 additions & 7 deletions src/command_validate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
#include "utils.h"

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

const auto &schema_path{options.at("").at(0)};
const auto dialect{default_dialect(options)};
const auto custom_resolver{resolver(
options, options.contains("h") || options.contains("http"), dialect)};
const auto dialect{infer_default_dialect(options)};
const auto custom_resolver{infer_resolver(options, dialect)};

const auto schema{sourcemeta::jsonschema::cli::read_file(schema_path)};
const auto schema{read_yaml_or_json(schema_path)};

if (!sourcemeta::core::is_schema(schema)) {
std::cerr << "error: The schema file you provided does not represent a "
Expand Down Expand Up @@ -138,8 +136,7 @@ auto sourcemeta::jsonschema::cli::validate(
log_verbose(options) << "warning: The JSONL file is empty\n";
}
} else {
const auto instance{
sourcemeta::jsonschema::cli::read_file(instance_path)};
const auto instance{read_yaml_or_json(instance_path)};
std::ostringstream error;
sourcemeta::blaze::SimpleOutput output{instance};
sourcemeta::blaze::TraceOutput trace_output;
Expand Down
Loading
Loading