Skip to content

Commit 90b8b6f

Browse files
committed
Normalise error message metadata
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
1 parent efea99a commit 90b8b6f

67 files changed

Lines changed: 161 additions & 121 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/command_lint.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ static auto get_lint_callback(sourcemeta::core::JSON &errors_array,
9292

9393
std::cout << ":\n";
9494
std::cout << " " << message << " (" << name << ")\n";
95-
std::cout << " at schema location \"";
95+
std::cout << " at location \"";
9696
sourcemeta::core::stringify(schema_location, std::cout);
9797
std::cout << "\"\n";
9898

src/command_test.cc

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,30 +66,30 @@ auto sourcemeta::jsonschema::test(const sourcemeta::core::Options &options)
6666
sourcemeta::core::read_yaml_or_json(entry.first)};
6767

6868
if (!test.is_object()) {
69-
std::cout << entry.first.string() << ":";
69+
std::cout << entry.first.string() << ":\n";
7070
throw TestError{"The test document must be an object", std::nullopt};
7171
}
7272

7373
if (!test.defines("target")) {
74-
std::cout << entry.first.string() << ":";
74+
std::cout << entry.first.string() << ":\n";
7575
throw TestError{"The test document must contain a `target` property",
7676
std::nullopt};
7777
}
7878

7979
if (!test.at("target").is_string()) {
80-
std::cout << entry.first.string() << ":";
80+
std::cout << entry.first.string() << ":\n";
8181
throw TestError{"The test document `target` property must be a URI",
8282
std::nullopt};
8383
}
8484

8585
if (!test.defines("tests")) {
86-
std::cout << entry.first.string() << ":";
86+
std::cout << entry.first.string() << ":\n";
8787
throw TestError{"The test document must contain a `tests` property",
8888
std::nullopt};
8989
}
9090

9191
if (!test.at("tests").is_array()) {
92-
std::cout << entry.first.string() << ":";
92+
std::cout << entry.first.string() << ":\n";
9393
throw TestError{"The test document `tests` property must be an array",
9494
std::nullopt};
9595
}
@@ -147,40 +147,47 @@ auto sourcemeta::jsonschema::test(const sourcemeta::core::Options &options)
147147
index += 1;
148148

149149
if (!test_case.is_object()) {
150+
std::cout << "\n";
150151
throw TestError{"Test case documents must be objects", index};
151152
}
152153

153154
if (!test_case.defines("data") && !test_case.defines("dataPath")) {
155+
std::cout << "\n";
154156
throw TestError{
155157
"Test case documents must contain a `data` or `dataPath` property",
156158
index};
157159
}
158160

159161
if (test_case.defines("data") && test_case.defines("dataPath")) {
162+
std::cout << "\n";
160163
throw TestError{"Test case documents must contain either a `data` or "
161164
"`dataPath` property, but not both",
162165
index};
163166
}
164167

165168
if (test_case.defines("dataPath") &&
166169
!test_case.at("dataPath").is_string()) {
170+
std::cout << "\n";
167171
throw TestError{
168172
"Test case documents must set the `dataPath` property to a string",
169173
index};
170174
}
171175

172176
if (test_case.defines("description") &&
173177
!test_case.at("description").is_string()) {
178+
std::cout << "\n";
174179
throw TestError{
175180
"If you set a test case description, it must be a string", index};
176181
}
177182

178183
if (!test_case.defines("valid")) {
184+
std::cout << "\n";
179185
throw TestError{"Test case documents must contain a `valid` property",
180186
index};
181187
}
182188

183189
if (!test_case.at("valid").is_boolean()) {
190+
std::cout << "\n";
184191
throw TestError{
185192
"The test case document `valid` property must be a boolean", index};
186193
}

src/error.h

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
#include <sourcemeta/core/schemaconfig.h>
88

99
#include <cassert> // assert
10+
#include <cstdlib> // EXIT_FAILURE
1011
#include <filesystem> // std::filesystem
1112
#include <functional> // std::function
13+
#include <iostream> // std::cout, std::cerr
1214
#include <optional> // std::optional
1315
#include <stdexcept> // std::runtime_error
1416
#include <string> // std::string
@@ -163,12 +165,12 @@ inline auto try_catch(const std::function<int()> &callback) noexcept -> int {
163165
<< " " << error.example() << "\n";
164166
return EXIT_FAILURE;
165167
} catch (const sourcemeta::jsonschema::NotSchemaError &error) {
166-
std::cerr << "error: " << error.what() << "\n "
168+
std::cerr << "error: " << error.what() << "\n at file path "
167169
<< sourcemeta::core::weakly_canonical(error.path()).string()
168170
<< "\n";
169171
return EXIT_FAILURE;
170172
} catch (const sourcemeta::jsonschema::YAMLInputError &error) {
171-
std::cerr << error.what() << "\n "
173+
std::cerr << error.what() << "\n at file path "
172174
<< sourcemeta::core::weakly_canonical(error.path()).string()
173175
<< "\n";
174176
return EXIT_FAILURE;
@@ -177,14 +179,14 @@ inline auto try_catch(const std::function<int()> &callback) noexcept -> int {
177179
return EXIT_FAILURE;
178180
} catch (const sourcemeta::jsonschema::InvalidLintRuleError &error) {
179181
std::cerr << "error: " << error.what() << "\n";
180-
std::cerr << " " << error.rule() << "\n";
182+
std::cerr << " at rule " << error.rule() << "\n";
181183
return EXIT_FAILURE;
182184
} catch (const sourcemeta::jsonschema::LintAutoFixError &error) {
183185
std::cerr << "error: " << error.what() << "\n";
184-
std::cerr << " at "
186+
std::cerr << " at file path "
185187
<< sourcemeta::core::weakly_canonical(error.path()).string()
186188
<< "\n";
187-
std::cerr << " at schema location \""
189+
std::cerr << " at location \""
188190
<< sourcemeta::core::to_string(error.location()) << "\"\n\n";
189191
std::cerr << "This is an unexpected error, as making the auto-fix "
190192
"functionality work in all\n";
@@ -203,7 +205,7 @@ inline auto try_catch(const std::function<int()> &callback) noexcept -> int {
203205
std::cerr << "Use '--help' for usage information\n";
204206
return EXIT_FAILURE;
205207
} catch (const sourcemeta::jsonschema::TestError &error) {
206-
std::cout << "\nerror: " << error.what() << "\n";
208+
std::cout << "error: " << error.what() << "\n";
207209
if (error.test_number().has_value()) {
208210
std::cout << " at test case #" << error.test_number().value() << "\n";
209211
}
@@ -213,34 +215,37 @@ inline auto try_catch(const std::function<int()> &callback) noexcept -> int {
213215
"docs/test.markdown\n";
214216
return EXIT_FAILURE;
215217
} catch (const sourcemeta::core::SchemaReferenceError &error) {
216-
std::cerr << "error: " << error.what() << "\n " << error.id()
217-
<< "\n at schema location \"";
218+
std::cerr << "error: " << error.what() << "\n at identifier " << error.id()
219+
<< "\n at location \"";
218220
sourcemeta::core::stringify(error.location(), std::cerr);
219221
std::cerr << "\"\n";
220222
return EXIT_FAILURE;
221223
} catch (const sourcemeta::jsonschema::FileError<
222224
sourcemeta::core::SchemaConfigParseError> &error) {
223-
std::cerr << "error: " << error.what() << "\n at "
225+
std::cerr << "error: " << error.what() << "\n at file path "
224226
<< sourcemeta::core::weakly_canonical(error.path()).string()
225227
<< "\n";
226228
std::cerr << " at location \""
227229
<< sourcemeta::core::to_string(error.location()) << "\"\n";
228230
return EXIT_FAILURE;
229231
} catch (const sourcemeta::jsonschema::FileError<
230232
sourcemeta::core::SchemaRelativeMetaschemaResolutionError> &error) {
231-
std::cerr << "error: " << error.what() << "\n uri " << error.id() << "\n";
232-
std::cerr << " at "
233+
std::cerr << "error: " << error.what() << "\n at identifier " << error.id()
234+
<< "\n";
235+
std::cerr << " at file path "
233236
<< sourcemeta::core::weakly_canonical(error.path()).string()
234237
<< "\n";
235238
return EXIT_FAILURE;
236239
} catch (
237240
const sourcemeta::core::SchemaRelativeMetaschemaResolutionError &error) {
238-
std::cerr << "error: " << error.what() << "\n " << error.id() << "\n";
241+
std::cerr << "error: " << error.what() << "\n at identifier " << error.id()
242+
<< "\n";
239243
return EXIT_FAILURE;
240244
} catch (const sourcemeta::jsonschema::FileError<
241245
sourcemeta::core::SchemaResolutionError> &error) {
242-
std::cerr << "error: " << error.what() << "\n uri " << error.id() << "\n";
243-
std::cerr << " at "
246+
std::cerr << "error: " << error.what() << "\n at identifier " << error.id()
247+
<< "\n";
248+
std::cerr << " at file path "
244249
<< sourcemeta::core::weakly_canonical(error.path()).string()
245250
<< "\n";
246251

@@ -253,7 +258,8 @@ inline auto try_catch(const std::function<int()> &callback) noexcept -> int {
253258

254259
return EXIT_FAILURE;
255260
} catch (const sourcemeta::core::SchemaResolutionError &error) {
256-
std::cerr << "error: " << error.what() << "\n " << error.id() << "\n";
261+
std::cerr << "error: " << error.what() << "\n at identifier " << error.id()
262+
<< "\n";
257263

258264
if (error.id().starts_with("file://")) {
259265
std::cerr << "\nThis is likely because the file does not exist\n";
@@ -272,7 +278,7 @@ inline auto try_catch(const std::function<int()> &callback) noexcept -> int {
272278
} catch (const sourcemeta::jsonschema::FileError<
273279
sourcemeta::core::SchemaUnknownBaseDialectError> &error) {
274280
std::cerr << "error: " << error.what() << "\n";
275-
std::cerr << " at "
281+
std::cerr << " at file path "
276282
<< sourcemeta::core::weakly_canonical(error.path()).string()
277283
<< "\n";
278284
std::cerr << "\nAre you sure the input is a valid JSON Schema and its "
@@ -294,41 +300,44 @@ inline auto try_catch(const std::function<int()> &callback) noexcept -> int {
294300
} catch (
295301
const sourcemeta::jsonschema::FileError<sourcemeta::core::SchemaError>
296302
&error) {
297-
std::cerr << "error: " << error.what() << "\n at "
303+
std::cerr << "error: " << error.what() << "\n at file path "
298304
<< sourcemeta::core::weakly_canonical(error.path()).string()
299305
<< "\n";
300306
return EXIT_FAILURE;
301307
} catch (const sourcemeta::core::SchemaError &error) {
302308
std::cerr << "error: " << error.what() << "\n";
303309
return EXIT_FAILURE;
304310
} catch (const sourcemeta::core::SchemaVocabularyError &error) {
305-
std::cerr << "error: " << error.what() << "\n " << error.uri()
311+
std::cerr << "error: " << error.what() << "\n at uri " << error.uri()
306312
<< "\n\nTo request support for it, please open an issue "
307313
"at\nhttps://github.com/sourcemeta/jsonschema\n";
308314
return EXIT_FAILURE;
309315
} catch (const sourcemeta::core::URIParseError &error) {
310-
std::cerr << "error: " << error.what() << " at column " << error.column()
311-
<< "\n";
316+
std::cerr << "error: " << error.what() << "\n";
317+
std::cerr << " at column " << error.column() << "\n";
312318
return EXIT_FAILURE;
313319
} catch (const sourcemeta::core::JSONFileParseError &error) {
314-
std::cerr << "error: " << error.what() << " at line " << error.line()
315-
<< " and column " << error.column() << "\n "
320+
std::cerr << "error: " << error.what() << "\n";
321+
std::cerr << " at line " << error.line() << "\n";
322+
std::cerr << " at column " << error.column() << "\n";
323+
std::cerr << " at file path "
316324
<< sourcemeta::core::weakly_canonical(error.path()).string()
317325
<< "\n";
318326
return EXIT_FAILURE;
319327
} catch (const sourcemeta::core::JSONParseError &error) {
320-
std::cerr << "error: " << error.what() << " at line " << error.line()
321-
<< " and column " << error.column() << "\n";
328+
std::cerr << "error: " << error.what() << "\n";
329+
std::cerr << " at line " << error.line() << "\n";
330+
std::cerr << " at column " << error.column() << "\n";
322331
return EXIT_FAILURE;
323332
} catch (const std::filesystem::filesystem_error &error) {
324333
// See https://en.cppreference.com/w/cpp/error/errc
325334
if (error.code() == std::errc::no_such_file_or_directory) {
326-
std::cerr << "error: " << error.code().message() << "\n "
335+
std::cerr << "error: " << error.code().message() << "\n at file path "
327336
<< sourcemeta::core::weakly_canonical(error.path1()).string()
328337
<< "\n";
329338
} else if (error.code() == std::errc::is_a_directory) {
330339
std::cerr << "error: The input was supposed to be a file but it is a "
331-
"directory\n "
340+
"directory\n at file path "
332341
<< sourcemeta::core::weakly_canonical(error.path1()).string()
333342
<< "\n";
334343
} else {

test/bundle/fail_bigint.sh

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@ EOF
3030
test "$CODE" = "1" || exit 1
3131

3232
cat << EOF > "$TMP/expected.txt"
33-
error: The JSON value is not representable by the IETF RFC 8259 interoperable signed integer range at line 5 and column 23
34-
$(realpath "$TMP")/invalid.json
33+
error: The JSON value is not representable by the IETF RFC 8259 interoperable signed integer range
34+
at line 5
35+
at column 23
36+
at file path $(realpath "$TMP")/invalid.json
3537
EOF
3638

3739
diff "$TMP/stderr.txt" "$TMP/expected.txt"

test/bundle/fail_relative_external_ref_missing.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ test "$CODE" = "1" || exit 1
2020

2121
cat << EOF > "$TMP/expected.txt"
2222
error: Could not resolve the reference to an external schema
23-
https://example.com/nested
23+
at identifier https://example.com/nested
2424
2525
This is likely because you forgot to import such schema using --resolve/-r
2626
EOF

test/bundle/fail_resolve_invalid_json.sh

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ EOF
2525
test "$CODE" = "1" || exit 1
2626

2727
cat << EOF > "$TMP/expected.txt"
28-
error: Failed to parse the JSON document at line 1 and column 3
29-
$(realpath "$TMP")/invalid.json
28+
error: Failed to parse the JSON document
29+
at line 1
30+
at column 3
31+
at file path $(realpath "$TMP")/invalid.json
3032
EOF
3133

3234
diff "$TMP/stderr.txt" "$TMP/expected.txt"

test/bundle/fail_schema_invalid_json.sh

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ EOF
1818
test "$CODE" = "1" || exit 1
1919

2020
cat << EOF > "$TMP/expected.txt"
21-
error: Failed to parse the JSON document at line 2 and column 10
22-
$(realpath "$TMP")/schema.json
21+
error: Failed to parse the JSON document
22+
at line 2
23+
at column 10
24+
at file path $(realpath "$TMP")/schema.json
2325
EOF
2426

2527
diff "$TMP/stderr.txt" "$TMP/expected.txt"

test/bundle/fail_unknown_metaschema.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ test "$CODE" = "1" || exit 1
2020

2121
cat << EOF > "$TMP/expected.txt"
2222
error: Could not resolve the metaschema of the schema
23-
https://example.com/unknown
23+
at identifier https://example.com/unknown
2424
2525
This is likely because you forgot to import such schema using --resolve/-r
2626
EOF

test/ci/fail_bundle_http_non_schema.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ test "$CODE" = "1" || exit 1
2121
cat << EOF > "$TMP/expected.txt"
2222
error: The JSON document is not a valid JSON Schema
2323
https://jsonplaceholder.typicode.com/todos/1
24-
at schema location "/allOf/0/\$ref"
24+
at location "/allOf/0/\$ref"
2525
EOF
2626

2727
diff "$TMP/stderr.txt" "$TMP/expected.txt"

test/ci/fail_bundle_http_non_schema_verbose.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ cat << EOF > "$TMP/expected.txt"
2222
Resolving over HTTP: https://jsonplaceholder.typicode.com/todos/1
2323
error: The JSON document is not a valid JSON Schema
2424
https://jsonplaceholder.typicode.com/todos/1
25-
at schema location "/allOf/0/\$ref"
25+
at location "/allOf/0/\$ref"
2626
EOF
2727

2828
diff "$TMP/stderr.txt" "$TMP/expected.txt"

0 commit comments

Comments
 (0)