Skip to content
Merged
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
4 changes: 3 additions & 1 deletion docs/test.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ as JSON files that follow a specific format:
into the resolution context using the `--resolve` or `--http` options_.

If the `target` is relative, it will be interpreted as a file path relative
to the test file location.
to the test file location. A test document read from standard input has no
location of its own, so its relative paths are interpreted against the
current working directory instead.

This property may also be set to a non-empty array of URIs, in which case
every test case in `tests` will be run against each of the listed schemas
Expand Down
8 changes: 4 additions & 4 deletions src/command_bundle.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ auto sourcemeta::jsonschema::bundle(const sourcemeta::core::Options &options)
throw sourcemeta::core::IOIsADirectoryError{schema_path};
}

const auto schema_resolution_base{
const auto schema_config_base{
schema_from_stdin ? std::filesystem::current_path() : schema_path};
const auto schema_display_path{schema_from_stdin ? stdin_path()
: schema_path};

const auto configuration_path{find_configuration(schema_resolution_base)};
const auto configuration_path{find_configuration(schema_config_base)};
const auto &configuration{
read_configuration(options, configuration_path, schema_resolution_base)};
read_configuration(options, configuration_path, schema_config_base)};
const auto dialect{default_dialect(options, configuration)};
auto parsed_schema{schema_from_stdin ? read_from_stdin()
: read_file(schema_path)};
Expand All @@ -57,7 +57,7 @@ auto sourcemeta::jsonschema::bundle(const sourcemeta::core::Options &options)
sourcemeta::blaze::bundle(
schema, sourcemeta::blaze::schema_walker, custom_resolver,
sourcemeta::blaze::BundleMode::NonOfficialMetaschemas, dialect,
sourcemeta::jsonschema::default_id(schema_resolution_base));
sourcemeta::jsonschema::default_id(schema_path, schema_from_stdin));

if (options.contains("without-id")) {
sourcemeta::jsonschema::LOG_WARNING()
Expand Down
2 changes: 1 addition & 1 deletion src/command_codegen.cc
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ auto sourcemeta::jsonschema::codegen(const sourcemeta::core::Options &options)
result = sourcemeta::blaze::compile(
schema, sourcemeta::blaze::schema_walker, custom_resolver,
sourcemeta::blaze::default_compiler, dialect,
sourcemeta::jsonschema::default_id(schema_path));
sourcemeta::jsonschema::default_id(schema_path, false));
} catch (const sourcemeta::blaze::SchemaKeywordError &error) {
throw sourcemeta::core::FileError<sourcemeta::blaze::SchemaKeywordError>(
schema_path, error);
Expand Down
3 changes: 2 additions & 1 deletion src/command_compile.cc
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ auto sourcemeta::jsonschema::compile(const sourcemeta::core::Options &options)
const auto fast_mode{options.contains("fast")};
const auto &custom_resolver{
resolver(options, options.contains("http"), dialect, configuration)};
const auto schema_default_id{sourcemeta::jsonschema::default_id(schema_path)};
const auto schema_default_id{
sourcemeta::jsonschema::default_id(schema_path, false)};

sourcemeta::blaze::Template schema_template;
try {
Expand Down
6 changes: 3 additions & 3 deletions src/command_inspect.cc
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,9 @@ auto sourcemeta::jsonschema::inspect(const sourcemeta::core::Options &options)
// Only use the file-based URI if the schema has no
// identifier, as otherwise we make the output unnecessarily
// hard when it comes to debugging schemas
!identifier.empty()
? ""
: sourcemeta::jsonschema::default_id(schema_resolution_base));
!identifier.empty() ? ""
: sourcemeta::jsonschema::default_id(
schema_resolution_base, schema_from_stdin));
} catch (const sourcemeta::blaze::SchemaKeywordError &error) {
throw sourcemeta::core::FileError<sourcemeta::blaze::SchemaKeywordError>(
schema_resolution_base, error);
Expand Down
2 changes: 1 addition & 1 deletion src/command_lint.cc
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ static auto get_lint_callback(sourcemeta::core::JSON &errors_array,
errors_array.push_back(error_obj);
} else {
if (entry.from_stdin) {
std::cout << "/dev/stdin";
std::cout << sourcemeta::jsonschema::STDIN_DEFAULT_ID;
} else {
std::cout
<< std::filesystem::relative(entry.resolution_base).string();
Expand Down
13 changes: 4 additions & 9 deletions src/command_rdf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ auto sourcemeta::jsonschema::rdf(const sourcemeta::core::Options &options)
const auto &custom_resolver{
resolver(options, options.contains("http"), dialect, configuration)};
const auto fast_mode{options.contains("fast")};
const auto schema_default_id{
sourcemeta::jsonschema::default_id(schema_resolution_base)};
const auto schema_default_id{sourcemeta::jsonschema::default_id(
schema_resolution_base, schema_from_stdin)};

const auto bundled{
bundle_for_evaluation(schema, custom_resolver, dialect, schema_default_id,
Expand Down Expand Up @@ -126,7 +126,7 @@ auto sourcemeta::jsonschema::rdf(const sourcemeta::core::Options &options)
const auto &instance{parsed_instance.document};
const auto instance_display_path{
instance_from_stdin
? stdin_path().string()
? std::string{STDIN_DEFAULT_ID}
: sourcemeta::core::weakly_canonical(instance_path).string()};

sourcemeta::blaze::Evaluator evaluator;
Expand Down Expand Up @@ -218,12 +218,7 @@ auto sourcemeta::jsonschema::rdf(const sourcemeta::core::Options &options)
}

LOG_VERBOSE(options) << "ok: " << instance_display_path << "\n matches "
<< (schema_from_stdin
? stdin_path().string()
: sourcemeta::core::weakly_canonical(
schema_resolution_base)
.string())
<< "\n";
<< stdin_path_string(schema_resolution_base) << "\n";
sourcemeta::core::prettify(document, std::cout);
std::cout << "\n";
}
7 changes: 6 additions & 1 deletion src/command_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,12 @@ auto parse_test_suite(const sourcemeta::jsonschema::InputJSON &entry,
-> sourcemeta::blaze::TestSuite {
try {
return sourcemeta::blaze::TestSuite::parse(
entry.second, entry.positions, entry.resolution_base.parent_path(),
entry.second, entry.positions,
// A test document read from standard input has no directory of its
// own, and the base path must remain a real directory, as relative
// `dataPath` and `rdfPath` entries are opened from it
entry.from_stdin ? std::filesystem::current_path()
: entry.resolution_base.parent_path(),
schema_resolver, sourcemeta::blaze::schema_walker,
sourcemeta::blaze::default_schema_compiler, dialect, "", tweaks);
} catch (const sourcemeta::blaze::TestParseError &error) {
Expand Down
15 changes: 8 additions & 7 deletions src/command_upgrade.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,14 @@ auto sourcemeta::jsonschema::upgrade(const sourcemeta::core::Options &options)
throw sourcemeta::core::IOIsADirectoryError{schema_path};
}

const auto schema_resolution_base{
const auto schema_config_base{
schema_from_stdin ? std::filesystem::current_path() : schema_path};
const auto schema_display_path{schema_from_stdin ? stdin_path()
: schema_path};

const auto configuration_path{find_configuration(schema_resolution_base)};
const auto configuration_path{find_configuration(schema_config_base)};
const auto &configuration{
read_configuration(options, configuration_path, schema_resolution_base)};
read_configuration(options, configuration_path, schema_config_base)};
const auto dialect{default_dialect(options, configuration)};
auto parsed_schema{schema_from_stdin ? read_from_stdin()
: read_file(schema_path)};
Expand All @@ -91,9 +91,9 @@ auto sourcemeta::jsonschema::upgrade(const sourcemeta::core::Options &options)
sourcemeta::blaze::SchemaFrame::Mode::Locations};

try {
frame.analyse(schema, sourcemeta::blaze::schema_walker, custom_resolver,
dialect,
sourcemeta::jsonschema::default_id(schema_resolution_base));
frame.analyse(
schema, sourcemeta::blaze::schema_walker, custom_resolver, dialect,
sourcemeta::jsonschema::default_id(schema_path, schema_from_stdin));
} catch (const sourcemeta::blaze::SchemaKeywordError &error) {
throw sourcemeta::core::FileError<sourcemeta::blaze::SchemaKeywordError>(
schema_display_path, error);
Expand Down Expand Up @@ -198,7 +198,8 @@ auto sourcemeta::jsonschema::upgrade(const sourcemeta::core::Options &options)
std::ignore = transformer.apply(
schema, sourcemeta::blaze::schema_walker, custom_resolver,
[](const auto &, const auto, const auto, const auto &, const auto) {},
dialect, sourcemeta::jsonschema::default_id(schema_resolution_base), "",
dialect,
sourcemeta::jsonschema::default_id(schema_path, schema_from_stdin), "",
options.contains("meta"));
}

Expand Down
24 changes: 8 additions & 16 deletions src/command_validate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,7 @@ auto process_entry(
const sourcemeta::jsonschema::CustomResolver &custom_resolver,
const sourcemeta::blaze::SchemaFrame &frame, bool benchmark,
std::uint64_t benchmark_loop, bool trace, bool fast_mode, bool json_output,
bool continue_on_error, bool schema_from_stdin,
const std::filesystem::path &schema_resolution_base,
bool continue_on_error, const std::filesystem::path &schema_resolution_base,
const sourcemeta::core::Options &options, bool &result) -> bool {
std::ostringstream error;
sourcemeta::blaze::SimpleOutput output{entry.second};
Expand Down Expand Up @@ -205,10 +204,7 @@ auto process_entry(
}
sourcemeta::jsonschema::LOG_VERBOSE(options)
<< "\n matches "
<< (schema_from_stdin
? "/dev/stdin"
: sourcemeta::core::weakly_canonical(schema_resolution_base)
.string())
<< sourcemeta::jsonschema::stdin_path_string(schema_resolution_base)
<< "\n";
sourcemeta::jsonschema::print_annotations(output, options, entry.positions,
std::cerr);
Expand Down Expand Up @@ -306,8 +302,8 @@ auto sourcemeta::jsonschema::validate(const sourcemeta::core::Options &options)
"Re-compile the template with --format-assertion instead"};
}

const auto schema_default_id{
sourcemeta::jsonschema::default_id(schema_resolution_base)};
const auto schema_default_id{sourcemeta::jsonschema::default_id(
schema_resolution_base, schema_from_stdin)};

const auto bundled{
bundle_for_evaluation(schema, custom_resolver, dialect, schema_default_id,
Expand Down Expand Up @@ -374,8 +370,8 @@ auto sourcemeta::jsonschema::validate(const sourcemeta::core::Options &options)
for (const auto &entry : for_each_json({}, options)) {
if (!process_entry(entry, evaluator, schema_template, custom_resolver,
frame, benchmark, benchmark_loop, trace, fast_mode,
json_output, continue_on_error, schema_from_stdin,
schema_resolution_base, options, result)) {
json_output, continue_on_error, schema_resolution_base,
options, result)) {
break;
}
}
Expand Down Expand Up @@ -409,7 +405,7 @@ auto sourcemeta::jsonschema::validate(const sourcemeta::core::Options &options)
for (const auto &entry : for_each_json({instance_path_view}, options)) {
if (!process_entry(entry, evaluator, schema_template, custom_resolver,
frame, benchmark, benchmark_loop, trace, fast_mode,
json_output, continue_on_error, schema_from_stdin,
json_output, continue_on_error,
schema_resolution_base, options, result)) {
break;
}
Expand Down Expand Up @@ -474,11 +470,7 @@ auto sourcemeta::jsonschema::validate(const sourcemeta::core::Options &options)
LOG_VERBOSE(options)
<< "ok: "
<< sourcemeta::core::weakly_canonical(instance_path).string()
<< "\n matches "
<< (schema_from_stdin ? "/dev/stdin"
: sourcemeta::core::weakly_canonical(
schema_resolution_base)
.string())
<< "\n matches " << stdin_path_string(schema_resolution_base)
<< "\n";
print_annotations(output, options, tracker, std::cerr);
} else {
Expand Down
31 changes: 18 additions & 13 deletions src/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <optional> // std::optional
#include <stdexcept> // std::runtime_error
#include <string> // std::string
#include <string_view> // std::string_view
#include <type_traits> // std::is_base_of_v, std::is_same_v
#include <utility> // std::forward, std::move
#include <vector> // std::vector
Expand Down Expand Up @@ -462,24 +463,28 @@ class Fail : public std::runtime_error {
int exit_code_;
};

// Input read from standard input has no retrieval URI, so JSON Schema 2020-12
// section 9.1.1 and RFC 3986 section 5.1.4 let us pick an implementation
// specific default. We deliberately pick an RFC 4151 tag URI, as it cannot be
// mistaken for a locator the way a file URI can. This doubles as how we refer
// to standard input in output, so that we never print a path that does not
// exist and never differ across platforms
constexpr std::string_view STDIN_DEFAULT_ID{
"tag:sourcemeta.com,2026:jsonschema/stdin"};

// Input read from standard input never corresponds to a file, so we carry the
// identifier itself where a path would otherwise go. It is never resolved
// against the filesystem, as every such site is guarded on whether the input
// came from standard input
inline auto stdin_path() -> std::filesystem::path {
#ifdef _WIN32
return std::filesystem::path{"<stdin>"};
#else
return std::filesystem::path{"/dev/stdin"};
#endif
return std::filesystem::path{STDIN_DEFAULT_ID};
}

inline auto stdin_path_string(const std::filesystem::path &p) -> std::string {
#ifdef _WIN32
if (p.string() == "<stdin>") {
return "<stdin>";
}
#else
if (p == std::filesystem::path{"/dev/stdin"}) {
return "/dev/stdin";
if (p == stdin_path()) {
return std::string{STDIN_DEFAULT_ID};
}
#endif

return sourcemeta::core::weakly_canonical(p).string();
}

Expand Down
5 changes: 3 additions & 2 deletions src/input.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,9 @@ handle_json_entry(const std::filesystem::path &entry_path,
if (entry_path == "-") {
auto parsed{read_from_stdin()};
const auto path{stdin_path()};
result.push_back({path.string(), path, std::move(parsed.document),
std::move(parsed.positions), 0, false, parsed.yaml, true,
result.push_back({std::string{STDIN_DEFAULT_ID}, path,
std::move(parsed.document), std::move(parsed.positions),
0, false, parsed.yaml, true,
std::move(parsed.property_storage)});
return;
}
Expand Down
10 changes: 7 additions & 3 deletions src/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,12 @@

namespace sourcemeta::jsonschema {

inline auto default_id(const std::filesystem::path &schema_path)
-> std::string {
inline auto default_id(const std::filesystem::path &schema_path,
const bool from_stdin) -> std::string {
if (from_stdin) {
return std::string{STDIN_DEFAULT_ID};
}

return sourcemeta::core::URI::from_path(
sourcemeta::core::weakly_canonical(schema_path))
.recompose();
Expand Down Expand Up @@ -70,7 +74,7 @@ inline auto resolve_relative_uri(const std::string &value,
}

inline auto default_id(const InputJSON &entry) -> std::string {
return default_id(entry.resolution_base);
return default_id(entry.resolution_base, entry.from_stdin);
}

inline auto resolve_entrypoint(const sourcemeta::blaze::SchemaFrame &frame,
Expand Down
4 changes: 4 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ add_jsonschema_test_unix(validate/pass_stdin_schema)
add_jsonschema_test_unix(validate/pass_stdin_schema_verbose)
add_jsonschema_test_unix(validate/pass_stdin_schema_json)
add_jsonschema_test_unix(validate/pass_stdin_schema_no_id)
add_jsonschema_test_unix(validate/fail_stdin_relative_ref)
add_jsonschema_test_unix(validate/fail_stdin_schema_and_instance)
add_jsonschema_test_unix(validate/fail_stdin_schema_validation)
add_jsonschema_test_unix(validate/fail_stdin_schema_validation_no_id)
Expand Down Expand Up @@ -491,6 +492,7 @@ add_jsonschema_test_unix(test/fail_anchor_collision)
add_jsonschema_test_unix(test/pass_config_ignore)
add_jsonschema_test_unix(test/pass_stdin)
add_jsonschema_test_unix(test/pass_stdin_verbose)
add_jsonschema_test_unix(test/pass_stdin_data_path)
add_jsonschema_test_unix(test/fail_stdin_invalid_json)
add_jsonschema_test_unix(test/fail_stdin_not_object)
add_jsonschema_test_unix(test/fail_stdin_test_failure)
Expand Down Expand Up @@ -554,6 +556,8 @@ add_jsonschema_test_unix(bundle/pass_config_ignore)
add_jsonschema_test_unix(bundle/pass_ref_in_bundled_resolves_against_id)
add_jsonschema_test_unix(bundle/pass_resolve_deduplicate_embedded)
add_jsonschema_test_unix(bundle/pass_stdin)
add_jsonschema_test_unix(bundle/pass_stdin_no_id)
add_jsonschema_test_unix(bundle/pass_stdin_no_id_without_id)
add_jsonschema_test_unix(bundle/fail_stdin_not_schema)
add_jsonschema_test_unix(bundle/fail_stdin_invalid_json)
add_jsonschema_test_unix(bundle/fail_invalid_header)
Expand Down
4 changes: 2 additions & 2 deletions test/bundle/fail_stdin_invalid_json.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ cat << 'EOF' > "$TMP/expected.txt"
error: Failed to parse the JSON document
at line 1
at column 9
at file path /dev/stdin
at file path tag:sourcemeta.com,2026:jsonschema/stdin
EOF

diff "$TMP/stderr.txt" "$TMP/expected.txt"
Expand All @@ -32,7 +32,7 @@ cat << 'EOF' > "$TMP/expected.txt"
"error": "Failed to parse the JSON document",
"line": 1,
"column": 9,
"filePath": "/dev/stdin"
"filePath": "tag:sourcemeta.com,2026:jsonschema/stdin"
}
EOF

Expand Down
4 changes: 2 additions & 2 deletions test/bundle/fail_stdin_not_schema.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ test "$EXIT_CODE" = "4"

cat << 'EOF' > "$TMP/expected.txt"
error: The schema file you provided does not represent a valid JSON Schema
at file path /dev/stdin
at file path tag:sourcemeta.com,2026:jsonschema/stdin
EOF

diff "$TMP/stderr.txt" "$TMP/expected.txt"
Expand All @@ -28,7 +28,7 @@ test "$EXIT_CODE" = "4"
cat << 'EOF' > "$TMP/expected.txt"
{
"error": "The schema file you provided does not represent a valid JSON Schema",
"filePath": "/dev/stdin"
"filePath": "tag:sourcemeta.com,2026:jsonschema/stdin"
}
EOF

Expand Down
Loading
Loading