1
1
#include < sourcemeta/core/json.h>
2
2
#include < sourcemeta/core/jsonl.h>
3
- #include < sourcemeta/core/jsonschema.h>
4
3
#include < sourcemeta/core/jsonpointer.h>
4
+ #include < sourcemeta/core/jsonschema.h>
5
5
6
6
#include < sourcemeta/blaze/compiler.h>
7
7
#include < sourcemeta/blaze/evaluator.h>
8
8
9
- #include < chrono> // std::chrono
10
- #include < cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
9
+ #include < chrono> // std::chrono
10
+ #include < cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
11
11
#include < filesystem>
12
12
#include < iostream> // std::cerr
13
13
#include < optional> // std::optional
25
25
auto sourcemeta::jsonschema::cli::validate (
26
26
const std::span<const std::string> &arguments) -> int {
27
27
const auto options{
28
- parse_options (arguments, {" h" , " http" , " b" , " benchmark" , " t" , " trace" ,
29
- " p" , " path" })};
28
+ parse_options (arguments, {" h" , " http" , " b" , " benchmark" , " t" , " trace" })};
29
+
30
+ auto leftover = options.at (" " );
30
31
31
- if (options.at (" " ).size () < 1 ) {
32
+ // Attempt to extract a pointer value from leftover arguments:
33
+ // --path=<pointer>
34
+ // --path <pointer>
35
+ // -p <pointer>
36
+ std::optional<std::string> pointer_option;
37
+ for (auto it = leftover.begin (); it != leftover.end ();) {
38
+ const auto &arg = *it;
39
+
40
+ // a) --path=/something
41
+ if (arg.rfind (" --path=" , 0 ) == 0 ) {
42
+ pointer_option = arg.substr (7 );
43
+ it = leftover.erase (it);
44
+ }
45
+ // b) --path or -p, next token is the pointer
46
+ else if (arg == " --path" || arg == " -p" ) {
47
+ it = leftover.erase (it);
48
+ if (it != leftover.end ()) {
49
+ pointer_option = *it;
50
+ it = leftover.erase (it);
51
+ } else {
52
+ std::cerr << " error: The '--path' option requires a pointer.\n " ;
53
+ return EXIT_FAILURE;
54
+ }
55
+ } else {
56
+ ++it;
57
+ }
58
+ }
59
+
60
+ if (leftover.size () < 1 ) {
32
61
std::cerr
33
62
<< " error: This command expects a path to a schema and a path to an\n "
34
63
<< " instance to validate against the schema. For example:\n\n "
35
64
<< " jsonschema validate path/to/schema.json path/to/instance.json\n " ;
36
65
return EXIT_FAILURE;
37
66
}
38
-
39
- if (options.at (" " ).size () < 2 ) {
67
+ if (leftover.size () < 2 ) {
40
68
std::cerr
41
69
<< " error: In addition to the schema, you must also pass an argument\n "
42
70
<< " that represents the instance to validate against. For example:\n\n "
43
71
<< " jsonschema validate path/to/schema.json path/to/instance.json\n " ;
44
72
return EXIT_FAILURE;
45
73
}
46
74
47
- const auto &schema_path{options. at ( " " ). at ( 0 )} ;
75
+ const auto &schema_path = leftover[ 0 ] ;
48
76
const auto custom_resolver{
49
77
resolver (options, options.contains (" h" ) || options.contains (" http" ))};
50
78
51
79
const auto schema{sourcemeta::jsonschema::cli::read_file (schema_path)};
52
80
53
- // Extract optional JSON Pointer from --path/-p
54
- std::optional<std::string> pointer_option;
55
- if (options.contains (" p" ) && !options.at (" p" ).empty ()) {
56
- pointer_option = options.at (" p" ).at (0 );
57
- } else if (options.contains (" path" ) && !options.at (" path" ).empty ()) {
58
- pointer_option = options.at (" path" ).at (0 );
59
- }
60
-
61
- // Start by copying the entire schema, then override if pointer is given
62
81
sourcemeta::core::JSON resolved_schema{schema};
63
-
64
82
if (pointer_option.has_value ()) {
65
- // Convert pointer string -> sourcemeta::core::Pointer
66
83
const auto pointer = sourcemeta::core::to_pointer (pointer_option.value ());
67
- // Attempt to get the sub-schema
68
84
const auto *maybe_ptr = sourcemeta::core::try_get (schema, pointer);
69
- if (maybe_ptr == nullptr ) {
85
+ if (! maybe_ptr) {
70
86
std::cerr << " error: Failed to resolve JSON Pointer '"
71
87
<< pointer_option.value () << " ' in the provided schema\n "
72
88
<< std::filesystem::canonical (schema_path).string () << " \n " ;
@@ -78,9 +94,16 @@ auto sourcemeta::jsonschema::cli::validate(
78
94
79
95
// Validate that the final resolved_schema is indeed a valid JSON Schema
80
96
if (!sourcemeta::core::is_schema (resolved_schema)) {
81
- std::cerr << " error: The schema (or sub-schema) you provided does not\n "
82
- << " represent a valid JSON Schema\n "
83
- << std::filesystem::canonical (schema_path).string () << " \n " ;
97
+ if (!pointer_option.has_value ()) {
98
+ std::cerr << " error: The schema file you provided does not represent a "
99
+ " valid JSON Schema\n "
100
+ << std::filesystem::canonical (schema_path).string () << " \n " ;
101
+ } else {
102
+ std::cerr << " error: The sub-schema at pointer '"
103
+ << pointer_option.value ()
104
+ << " ' does not represent a valid JSON Schema\n "
105
+ << std::filesystem::canonical (schema_path).string () << " \n " ;
106
+ }
84
107
return EXIT_FAILURE;
85
108
}
86
109
@@ -93,10 +116,8 @@ auto sourcemeta::jsonschema::cli::validate(
93
116
94
117
bool result{true };
95
118
96
- auto iterator{options.at (" " ).cbegin ()};
97
- std::advance (iterator, 1 );
98
- for (; iterator != options.at (" " ).cend (); ++iterator) {
99
- const std::filesystem::path instance_path{*iterator};
119
+ for (std::size_t i = 1 ; i < leftover.size (); ++i) {
120
+ const std::filesystem::path instance_path{leftover[i]};
100
121
if (instance_path.extension () == " .jsonl" ) {
101
122
log_verbose (options)
102
123
<< " Interpreting input as JSONL: "
0 commit comments