diff --git a/docs/bundle.markdown b/docs/bundle.markdown index 7b08afeb..848d10ab 100644 --- a/docs/bundle.markdown +++ b/docs/bundle.markdown @@ -8,6 +8,7 @@ jsonschema bundle [--resolve/-r ...] [--extension/-e ] [--ignore/-i ] [--without-id/-w] [--default-dialect/-d ] [--json/-j] + [--indentation/-n ] [--configuration/-C ] [--color auto|always|never] ``` @@ -25,6 +26,9 @@ to resolve remote references in advance and inline them into the given schema for local consumption or further distribution. The JSON Schema CLI supports this functionality through the `bundle` command. +Use `--indentation/-n` to set how many spaces each level of nesting of the +result is laid out with, which defaults to two. + > [!WARNING] > A popular use case for JSON Schema is providing auto-completion for code > editors. If you plan to use your bundled schema for this, keep in mind that diff --git a/docs/upgrade.markdown b/docs/upgrade.markdown index 42024ffa..493a7f82 100644 --- a/docs/upgrade.markdown +++ b/docs/upgrade.markdown @@ -7,7 +7,7 @@ jsonschema upgrade [--to/-t draft4|draft6|draft7|2019-09|202 [--header/-H ": "] [--resolve/-r ...] [--default-dialect/-d ] [--configuration/-C ] - [--color auto|always|never] + [--indentation/-n ] [--color auto|always|never] ``` > [!NOTE] @@ -21,6 +21,9 @@ specifications into account, including re-writing references that point at locations whose path has changed. By default, schemas are upgraded to the latest supported dialect, and the result is printed to standard output. +Use `--indentation/-n` to set how many spaces each level of nesting of the +result is laid out with, which defaults to two. + For example, consider the following Draft 3 schema: ```json diff --git a/src/command_bundle.cc b/src/command_bundle.cc index 17a9ff2e..7a259378 100644 --- a/src/command_bundle.cc +++ b/src/command_bundle.cc @@ -22,6 +22,7 @@ auto sourcemeta::jsonschema::bundle(const sourcemeta::core::Options &options) } validate_http_headers(options); + const auto indentation{parse_indentation(options)}; const std::filesystem::path schema_path{options.positional().front()}; const bool schema_from_stdin = (schema_path == "-"); @@ -118,6 +119,6 @@ auto sourcemeta::jsonschema::bundle(const sourcemeta::core::Options &options) schema_display_path, error.what()); } - sourcemeta::core::prettify(schema, std::cout); + sourcemeta::core::prettify(schema, std::cout, indentation); std::cout << "\n"; } diff --git a/src/command_upgrade.cc b/src/command_upgrade.cc index 29bf6aac..9ff2d94c 100644 --- a/src/command_upgrade.cc +++ b/src/command_upgrade.cc @@ -159,6 +159,7 @@ auto sourcemeta::jsonschema::upgrade(const sourcemeta::core::Options &options) const auto target_value{options.contains("to") ? options.at("to").front() : std::string_view{"2020-12"}}; const auto target_dialect{parse_target_dialect(target_value)}; + const auto indentation{parse_indentation(options)}; const std::filesystem::path schema_path{options.positional().front()}; const bool schema_from_stdin = (schema_path == "-"); @@ -197,6 +198,6 @@ auto sourcemeta::jsonschema::upgrade(const sourcemeta::core::Options &options) sourcemeta::jsonschema::format_schema(schema, custom_resolver, dialect); - sourcemeta::core::prettify(schema, std::cout); + sourcemeta::core::prettify(schema, std::cout, indentation); std::cout << "\n"; } diff --git a/src/error.h b/src/error.h index 4c736fb3..4f6848ee 100644 --- a/src/error.h +++ b/src/error.h @@ -216,6 +216,13 @@ class InvalidJobsError : public std::runtime_error { : std::runtime_error{"The --jobs option must be a positive integer"} {} }; +class InvalidIndentationError : public std::runtime_error { +public: + InvalidIndentationError() + : std::runtime_error{ + "The --indentation option must be a non-negative integer"} {} +}; + class InvalidLintRuleError : public std::runtime_error { public: InvalidLintRuleError(const std::string &message, std::string rule) @@ -1652,6 +1659,10 @@ inline auto try_catch(const sourcemeta::core::Options &options, const auto is_json{options.contains("json")}; print_exception(is_json, error); return EXIT_INVALID_CLI_ARGUMENTS; + } catch (const InvalidIndentationError &error) { + const auto is_json{options.contains("json")}; + print_exception(is_json, error); + return EXIT_INVALID_CLI_ARGUMENTS; } catch (const InvalidOptionEnumerationValueError &error) { const auto is_json{options.contains("json")}; print_exception(is_json, error); diff --git a/src/main.cc b/src/main.cc index ccef365a..017fc03d 100644 --- a/src/main.cc +++ b/src/main.cc @@ -117,6 +117,7 @@ constexpr std::string_view USAGE_COMMANDS{R"EOF( version / --version / -v upgrade [--to/-t draft4|draft6|draft7|2019-09|2020-12] + [--indentation/-n ] Upgrade the given schema to a newer JSON Schema dialect. Defaults to the latest dialect (2020-12). Schemas that declare a @@ -125,6 +126,7 @@ constexpr std::string_view USAGE_COMMANDS{R"EOF( version / --version / -v bundle [--extension/-e ] [--ignore/-i ] [--without-id/-w] + [--indentation/-n ] Perform JSON Schema Bundling on a schema to inline remote references, printing the result to standard output. @@ -283,6 +285,7 @@ auto jsonschema_main(const std::string &program, const std::string &command, app.flag("without-id", {"w"}); app.option("extension", {"e"}); app.option("ignore", {"i"}); + app.option("indentation", {"n"}); parse_options(app, argc, argv, {.skip = 1}); sourcemeta::jsonschema::bundle(app); return EXIT_SUCCESS; @@ -387,6 +390,7 @@ auto jsonschema_main(const std::string &program, const std::string &command, if (command == "upgrade") { app.option("to", {"t"}); + app.option("indentation", {"n"}); parse_options(app, argc, argv, {.skip = 1}); sourcemeta::jsonschema::upgrade(app); return EXIT_SUCCESS; diff --git a/src/utils.h b/src/utils.h index 3eed8538..a00cd922 100644 --- a/src/utils.h +++ b/src/utils.h @@ -228,11 +228,22 @@ inline auto parse_jobs(const sourcemeta::core::Options &options) inline auto parse_indentation(const sourcemeta::core::Options &options) -> std::size_t { - if (options.contains("indentation")) { - return std::stoull(std::string{options.at("indentation").front()}); + if (!options.contains("indentation")) { + return 2; } - return 2; + const std::string value{options.at("indentation").front()}; + if (value.empty() || !std::ranges::all_of(value, [](const char character) { + return std::isdigit(static_cast(character)); + })) { + throw InvalidIndentationError{}; + } + + try { + return std::stoull(value); + } catch (const std::out_of_range &) { + throw InvalidIndentationError{}; + } } inline auto format_assertion_tweaks(const sourcemeta::core::Options &options) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 27fed952..5ccbe85e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -72,6 +72,7 @@ add_jsonschema_test(format/pass_yaml_anchor_without_alias) add_jsonschema_test(format/pass_yaml_anchors_keep_ordering) add_jsonschema_test(format/pass_yaml_keep_ordering) add_jsonschema_test(format/pass_yaml_indentation) +add_jsonschema_test(format/fail_invalid_indentation) add_jsonschema_test(format/pass_yaml_intact_mtime) add_jsonschema_test(format/pass_yaml_stdin) add_jsonschema_test(format/pass_yaml_directory) @@ -682,6 +683,8 @@ add_jsonschema_test(bundle/pass_2020_12_http) add_jsonschema_test(bundle/pass_resolve_single_default_dialect) add_jsonschema_test(bundle/pass_boolean_schema_default_dialect) add_jsonschema_test(bundle/pass_resolve_yaml) +add_jsonschema_test(bundle/pass_indentation) +add_jsonschema_test(bundle/fail_invalid_indentation) add_jsonschema_test(bundle/pass_resolve_with_ignore) add_jsonschema_test(bundle/pass_resolve_no_identifier) add_jsonschema_test(bundle/pass_without_id) @@ -874,6 +877,7 @@ add_jsonschema_test(lint/fail_lint_openapi_version) add_jsonschema_test(lint/fail_lint_openapi_version_newer) add_jsonschema_test(lint/fail_lint_openapi_version_malformed) add_jsonschema_test(lint/fail_lint_openapi_version_not_a_string) +add_jsonschema_test(lint/fail_lint_invalid_indentation) add_jsonschema_test(lint/fail_lint) add_jsonschema_test(lint/fail_lint_color_always) add_jsonschema_test(lint/fail_lint_color_never) @@ -1209,6 +1213,8 @@ add_jsonschema_test(upgrade/pass_default_dialect) add_jsonschema_test(upgrade/pass_configuration_option) add_jsonschema_test(upgrade/pass_resolve) add_jsonschema_test(upgrade/pass_yaml_input) +add_jsonschema_test(upgrade/pass_indentation) +add_jsonschema_test(upgrade/fail_invalid_indentation) add_jsonschema_test(upgrade/pass_embedded_resources) add_jsonschema_test(upgrade/fail_custom_metaschema_root) add_jsonschema_test(upgrade/fail_custom_metaschema_nested) diff --git a/test/bundle/fail_invalid_indentation.clitest b/test/bundle/fail_invalid_indentation.clitest new file mode 100644 index 00000000..d2a76a56 --- /dev/null +++ b/test/bundle/fail_invalid_indentation.clitest @@ -0,0 +1,27 @@ +WRITE schema.json UNTIL EOF +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.com", + "$ref": "nested" +} +EOF + +// Invalid CLI arguments +RUN bundle schema.json --indentation abc STDIN /dev/null IN . INTO result_0.txt EXPECTING 5 + +WRITE expected_0.txt UNTIL EOF +2> error: The --indentation option must be a non-negative integer +EOF + +COMPARE result_0.txt AGAINST expected_0.txt + +// Invalid CLI arguments +RUN bundle schema.json --indentation abc --json STDIN /dev/null IN . INTO result_1.txt EXPECTING 5 + +WRITE expected_1.txt UNTIL EOF +1> { +1> "error": "The --indentation option must be a non-negative integer" +1> } +EOF + +COMPARE result_1.txt AGAINST expected_1.txt diff --git a/test/bundle/pass_indentation.clitest b/test/bundle/pass_indentation.clitest new file mode 100644 index 00000000..c7274379 --- /dev/null +++ b/test/bundle/pass_indentation.clitest @@ -0,0 +1,34 @@ +WRITE schema.json UNTIL EOF +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.com", + "$ref": "nested" +} +EOF + +WRITE remote.json UNTIL EOF +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://example.com/nested", + "type": "string" +} +EOF + +RUN bundle schema.json --resolve remote.json --indentation 4 STDIN /dev/null IN . INTO result.txt EXPECTING 0 + +WRITE expected.txt UNTIL EOF +1> { +1> "$schema": "https://json-schema.org/draft/2020-12/schema", +1> "$id": "https://example.com", +1> "$ref": "nested", +1> "$defs": { +1> "https://example.com/nested": { +1> "$schema": "https://json-schema.org/draft/2020-12/schema", +1> "$id": "https://example.com/nested", +1> "type": "string" +1> } +1> } +1> } +EOF + +COMPARE result.txt AGAINST expected.txt diff --git a/test/format/fail_invalid_indentation.clitest b/test/format/fail_invalid_indentation.clitest new file mode 100644 index 00000000..7985db6a --- /dev/null +++ b/test/format/fail_invalid_indentation.clitest @@ -0,0 +1,26 @@ +WRITE schema.json UNTIL EOF +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "string" +} +EOF + +// Invalid CLI arguments +RUN fmt schema.json --indentation abc STDIN /dev/null IN . INTO result_0.txt EXPECTING 5 + +WRITE expected_0.txt UNTIL EOF +2> error: The --indentation option must be a non-negative integer +EOF + +COMPARE result_0.txt AGAINST expected_0.txt + +// Invalid CLI arguments +RUN fmt schema.json --indentation abc --json STDIN /dev/null IN . INTO result_1.txt EXPECTING 5 + +WRITE expected_1.txt UNTIL EOF +1> { +1> "error": "The --indentation option must be a non-negative integer" +1> } +EOF + +COMPARE result_1.txt AGAINST expected_1.txt diff --git a/test/help_command.clitest b/test/help_command.clitest index f0f9fb24..5483e098 100644 --- a/test/help_command.clitest +++ b/test/help_command.clitest @@ -127,6 +127,7 @@ WRITE expected.txt UNTIL EOF 1> 1> upgrade 1> [--to/-t draft4|draft6|draft7|2019-09|2020-12] +1> [--indentation/-n ] 1> 1> Upgrade the given schema to a newer JSON Schema dialect. 1> Defaults to the latest dialect (2020-12). Schemas that declare a @@ -135,6 +136,7 @@ WRITE expected.txt UNTIL EOF 1> 1> bundle [--extension/-e ] 1> [--ignore/-i ] [--without-id/-w] +1> [--indentation/-n ] 1> 1> Perform JSON Schema Bundling on a schema to inline remote references, 1> printing the result to standard output. diff --git a/test/help_option_long.clitest b/test/help_option_long.clitest index 151185bd..a92e348a 100644 --- a/test/help_option_long.clitest +++ b/test/help_option_long.clitest @@ -118,6 +118,7 @@ WRITE expected.txt UNTIL EOF 1> 1> [ANSI_BOLD_CYAN]upgrade[ANSI_RESET] 1> [--to/-t draft4|draft6|draft7|2019-09|2020-12] +1> [--indentation/-n ] 1> 1> Upgrade the given schema to a newer JSON Schema dialect. 1> Defaults to the latest dialect (2020-12). Schemas that declare a @@ -126,6 +127,7 @@ WRITE expected.txt UNTIL EOF 1> 1> [ANSI_BOLD_CYAN]bundle[ANSI_RESET] [--extension/-e ] 1> [--ignore/-i ] [--without-id/-w] +1> [--indentation/-n ] 1> 1> Perform JSON Schema Bundling on a schema to inline remote references, 1> printing the result to standard output. diff --git a/test/help_option_short.clitest b/test/help_option_short.clitest index 65962342..f2fcbcb0 100644 --- a/test/help_option_short.clitest +++ b/test/help_option_short.clitest @@ -115,6 +115,7 @@ WRITE expected.txt UNTIL EOF 1> 1> upgrade 1> [--to/-t draft4|draft6|draft7|2019-09|2020-12] +1> [--indentation/-n ] 1> 1> Upgrade the given schema to a newer JSON Schema dialect. 1> Defaults to the latest dialect (2020-12). Schemas that declare a @@ -123,6 +124,7 @@ WRITE expected.txt UNTIL EOF 1> 1> bundle [--extension/-e ] 1> [--ignore/-i ] [--without-id/-w] +1> [--indentation/-n ] 1> 1> Perform JSON Schema Bundling on a schema to inline remote references, 1> printing the result to standard output. diff --git a/test/lint/fail_lint_invalid_indentation.clitest b/test/lint/fail_lint_invalid_indentation.clitest new file mode 100644 index 00000000..4c2785c0 --- /dev/null +++ b/test/lint/fail_lint_invalid_indentation.clitest @@ -0,0 +1,26 @@ +WRITE schema.json UNTIL EOF +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "string" +} +EOF + +// Invalid CLI arguments +RUN lint schema.json --indentation abc STDIN /dev/null IN . INTO result_0.txt EXPECTING 5 + +WRITE expected_0.txt UNTIL EOF +2> error: The --indentation option must be a non-negative integer +EOF + +COMPARE result_0.txt AGAINST expected_0.txt + +// Invalid CLI arguments +RUN lint schema.json --indentation abc --json STDIN /dev/null IN . INTO result_1.txt EXPECTING 5 + +WRITE expected_1.txt UNTIL EOF +1> { +1> "error": "The --indentation option must be a non-negative integer" +1> } +EOF + +COMPARE result_1.txt AGAINST expected_1.txt diff --git a/test/upgrade/fail_invalid_indentation.clitest b/test/upgrade/fail_invalid_indentation.clitest new file mode 100644 index 00000000..bdfc5405 --- /dev/null +++ b/test/upgrade/fail_invalid_indentation.clitest @@ -0,0 +1,26 @@ +WRITE schema.json UNTIL EOF +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "string" +} +EOF + +// Invalid CLI arguments +RUN upgrade schema.json --indentation abc STDIN /dev/null IN . INTO result_0.txt EXPECTING 5 + +WRITE expected_0.txt UNTIL EOF +2> error: The --indentation option must be a non-negative integer +EOF + +COMPARE result_0.txt AGAINST expected_0.txt + +// Invalid CLI arguments +RUN upgrade schema.json --indentation abc --json STDIN /dev/null IN . INTO result_1.txt EXPECTING 5 + +WRITE expected_1.txt UNTIL EOF +1> { +1> "error": "The --indentation option must be a non-negative integer" +1> } +EOF + +COMPARE result_1.txt AGAINST expected_1.txt diff --git a/test/upgrade/pass_indentation.clitest b/test/upgrade/pass_indentation.clitest new file mode 100644 index 00000000..d4797d5e --- /dev/null +++ b/test/upgrade/pass_indentation.clitest @@ -0,0 +1,25 @@ +WRITE schema.json UNTIL EOF +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "https://example.com/test", + "properties": { + "foo": { "type": "string" } + } +} +EOF + +RUN upgrade schema.json --indentation 4 STDIN /dev/null IN . INTO result.txt EXPECTING 0 + +WRITE expected.txt UNTIL EOF +1> { +1> "$schema": "https://json-schema.org/draft/2020-12/schema", +1> "$id": "https://example.com/test", +1> "properties": { +1> "foo": { +1> "type": "string" +1> } +1> } +1> } +EOF + +COMPARE result.txt AGAINST expected.txt