Skip to content

Commit

Permalink
Support JSON output on the frame command with --json (#116)
Browse files Browse the repository at this point in the history
  • Loading branch information
Era-cell authored Jul 23, 2024
1 parent 47da7d0 commit f4963d4
Show file tree
Hide file tree
Showing 5 changed files with 268 additions and 81 deletions.
8 changes: 7 additions & 1 deletion docs/frame.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Framing
=======

```sh
jsonschema frame <schema.json> [--verbose/-v]
jsonschema frame <schema.json> [--json/-j] [--verbose/-v]
```

To evaluate a schema, an implementation will first scan it to determine the
Expand Down Expand Up @@ -60,3 +60,9 @@ reference:
```sh
jsonschema frame path/to/my/schema.json
```

### Frame a JSON Schema and output result as a JSON document

```sh
jsonschema frame path/to/my/schema.json --json
```
210 changes: 145 additions & 65 deletions src/command_frame.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,25 @@

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

#include "command.h"
#include "utils.h"

static auto enum_to_string(
const sourcemeta::jsontoolkit::ReferenceEntryType type) -> std::string {
switch (type) {
case sourcemeta::jsontoolkit::ReferenceEntryType::Resource:
return "resource";
case sourcemeta::jsontoolkit::ReferenceEntryType::Anchor:
return "anchor";
case sourcemeta::jsontoolkit::ReferenceEntryType::Pointer:
return "pointer";
default:
return "unknown";
}
}

auto intelligence::jsonschema::cli::frame(
const std::span<const std::string> &arguments) -> int {
const auto options{parse_options(arguments, {})};
Expand All @@ -27,75 +42,140 @@ auto intelligence::jsonschema::cli::frame(
resolver(options))
.wait();

for (const auto &[key, entry] : frame) {
switch (entry.type) {
case sourcemeta::jsontoolkit::ReferenceEntryType::Resource:
std::cout << "(LOCATION)";
break;
case sourcemeta::jsontoolkit::ReferenceEntryType::Anchor:
std::cout << "(ANCHOR)";
break;
case sourcemeta::jsontoolkit::ReferenceEntryType::Pointer:
std::cout << "(POINTER)";
break;
default:
// We should never get here
assert(false);
std::cout << "(UNKNOWN)";
break;
}

std::cout << " URI: ";
std::cout << key.second << "\n";

std::cout << " Type : ";
if (key.first == sourcemeta::jsontoolkit::ReferenceType::Dynamic) {
std::cout << "Dynamic";
} else {
std::cout << "Static";
const auto output_json = options.contains("json") || options.contains("j");
if (output_json) {
auto output_json_object = sourcemeta::jsontoolkit::JSON::make_object();
auto frame_json = sourcemeta::jsontoolkit::JSON::make_object();
auto references_json = sourcemeta::jsontoolkit::JSON::make_object();

for (const auto &[key, entry] : frame) {
auto frame_entry = sourcemeta::jsontoolkit::JSON::make_object();
if (entry.root.has_value()) {
frame_entry.assign("root",
sourcemeta::jsontoolkit::JSON{entry.root.value()});
} else {
frame_entry.assign("root", sourcemeta::jsontoolkit::JSON{nullptr});
}
std::ostringstream pointer_stream;
sourcemeta::jsontoolkit::stringify(entry.pointer, pointer_stream);
frame_entry.assign("pointer",
sourcemeta::jsontoolkit::JSON{pointer_stream.str()});
frame_entry.assign("base", sourcemeta::jsontoolkit::JSON{entry.base});
frame_entry.assign(
"type", sourcemeta::jsontoolkit::JSON{enum_to_string(entry.type)});
std::ostringstream reference_stream;
sourcemeta::jsontoolkit::stringify(entry.relative_pointer,
reference_stream);
frame_entry.assign("relativePointer",
sourcemeta::jsontoolkit::JSON{reference_stream.str()});
frame_entry.assign("dialect",
sourcemeta::jsontoolkit::JSON{entry.dialect});
frame_json.assign(key.second, sourcemeta::jsontoolkit::JSON{frame_entry});
}
std::cout << "\n";

std::cout << " Root : " << entry.root.value_or("<ANONYMOUS>")
<< "\n";
std::cout << " Pointer :";
if (!entry.pointer.empty()) {
std::cout << " ";
output_json_object.assign("frames",
sourcemeta::jsontoolkit::JSON{frame_json});

for (const auto &[pointer, entry] : references) {
auto ref_entry = sourcemeta::jsontoolkit::JSON::make_object();
ref_entry.assign(
"type",
sourcemeta::jsontoolkit::JSON{
pointer.first == sourcemeta::jsontoolkit::ReferenceType::Dynamic
? "dynamic"
: "static"});
ref_entry.assign("destination",
sourcemeta::jsontoolkit::JSON{entry.destination});
if (entry.base.has_value()) {
ref_entry.assign("base",
sourcemeta::jsontoolkit::JSON{entry.base.value()});
} else {
ref_entry.assign("base", sourcemeta::jsontoolkit::JSON{nullptr});
}
if (entry.fragment.has_value()) {
ref_entry.assign("fragment",
sourcemeta::jsontoolkit::JSON{entry.fragment.value()});
} else {
ref_entry.assign("fragment", sourcemeta::jsontoolkit::JSON{nullptr});
}
std::ostringstream ref_entry_stream;
sourcemeta::jsontoolkit::stringify(pointer.second, ref_entry_stream);
references_json.assign(ref_entry_stream.str(),
sourcemeta::jsontoolkit::JSON{ref_entry});
}

sourcemeta::jsontoolkit::stringify(entry.pointer, std::cout);
std::cout << "\n";
std::cout << " Base : " << entry.base << "\n";
std::cout << " Relative Pointer :";
if (!entry.relative_pointer.empty()) {
std::cout << " ";
}

sourcemeta::jsontoolkit::stringify(entry.relative_pointer, std::cout);
std::cout << "\n";
std::cout << " Dialect : " << entry.dialect << "\n";
}

for (const auto &[pointer, entry] : references) {
std::cout << "(REFERENCE) URI: ";
sourcemeta::jsontoolkit::stringify(pointer.second, std::cout);
std::cout << "\n";

std::cout << " Type : ";
if (pointer.first == sourcemeta::jsontoolkit::ReferenceType::Dynamic) {
std::cout << "Dynamic";
} else {
std::cout << "Static";
}
std::cout << "\n";
std::cout << " Destination : " << entry.destination << "\n";

if (entry.base.has_value()) {
std::cout << " - (w/o fragment) : " << entry.base.value() << "\n";
output_json_object.assign("references",
sourcemeta::jsontoolkit::JSON{references_json});

std::ostringstream print_stream;
sourcemeta::jsontoolkit::prettify(output_json_object, print_stream);
std::cout << print_stream.str() << std::endl;
} else {
for (const auto &[key, entry] : frame) {
switch (entry.type) {
case sourcemeta::jsontoolkit::ReferenceEntryType::Resource:
std::cout << "(LOCATION)";
break;
case sourcemeta::jsontoolkit::ReferenceEntryType::Anchor:
std::cout << "(ANCHOR)";
break;
case sourcemeta::jsontoolkit::ReferenceEntryType::Pointer:
std::cout << "(POINTER)";
break;
default:
// We should never get here
assert(false);
std::cout << "(UNKNOWN)";
break;
}

std::cout << " URI: ";
std::cout << key.second << "\n";

std::cout << " Type : ";
if (key.first == sourcemeta::jsontoolkit::ReferenceType::Dynamic) {
std::cout << "Dynamic";
} else {
std::cout << "Static";
}
std::cout << "\n";

std::cout << " Schema : "
<< entry.root.value_or("<ANONYMOUS>") << "\n";
std::cout << " Pointer :";
if (!entry.pointer.empty()) {
std::cout << " ";
}
sourcemeta::jsontoolkit::stringify(entry.pointer, std::cout);
std::cout << "\n";
std::cout << " Base URI : " << entry.base << "\n";
std::cout << " Relative Pointer :";
if (!entry.relative_pointer.empty()) {
std::cout << " ";
}
sourcemeta::jsontoolkit::stringify(entry.relative_pointer, std::cout);
std::cout << "\n";
std::cout << " Dialect : " << entry.dialect << "\n";
}

if (entry.fragment.has_value()) {
std::cout << " - (fragment) : " << entry.fragment.value() << "\n";
for (const auto &[pointer, entry] : references) {
std::cout << "(REFERENCE) URI: ";
sourcemeta::jsontoolkit::stringify(pointer.second, std::cout);
std::cout << "\n";
std::cout << " Type : "
<< (pointer.first ==
sourcemeta::jsontoolkit::ReferenceType::Dynamic
? "Dynamic"
: "Static")
<< "\n";
std::cout << " Destination : " << entry.destination << "\n";

if (entry.base.has_value()) {
std::cout << " - (w/o fragment) : " << entry.base.value() << "\n";
}

if (entry.fragment.has_value()) {
std::cout << " - (fragment) : " << entry.fragment.value()
<< "\n";
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Global Options:
Perform JSON Schema Bundling on a schema to inline remote references,
printing the result to standard output.
frame <schema.json>
frame <schema.json> [--json/-j]
Frame a schema in-place, displaying schema locations and references
in a human-readable manner.
Expand Down
28 changes: 14 additions & 14 deletions test/frame/pass.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,51 +23,51 @@ EOF
cat << 'EOF' > "$TMP/expected.txt"
(LOCATION) URI: https://example.com
Type : Static
Root : https://example.com
Schema : https://example.com
Pointer :
Base : https://example.com
Base URI : https://example.com
Relative Pointer :
Dialect : https://json-schema.org/draft/2020-12/schema
(POINTER) URI: https://example.com#/$defs
Type : Static
Root : https://example.com
Schema : https://example.com
Pointer : /$defs
Base : https://example.com
Base URI : https://example.com
Relative Pointer : /$defs
Dialect : https://json-schema.org/draft/2020-12/schema
(POINTER) URI: https://example.com#/$defs/string
Type : Static
Root : https://example.com
Schema : https://example.com
Pointer : /$defs/string
Base : https://example.com
Base URI : https://example.com
Relative Pointer : /$defs/string
Dialect : https://json-schema.org/draft/2020-12/schema
(POINTER) URI: https://example.com#/$defs/string/type
Type : Static
Root : https://example.com
Schema : https://example.com
Pointer : /$defs/string/type
Base : https://example.com
Base URI : https://example.com
Relative Pointer : /$defs/string/type
Dialect : https://json-schema.org/draft/2020-12/schema
(POINTER) URI: https://example.com#/$id
Type : Static
Root : https://example.com
Schema : https://example.com
Pointer : /$id
Base : https://example.com
Base URI : https://example.com
Relative Pointer : /$id
Dialect : https://json-schema.org/draft/2020-12/schema
(POINTER) URI: https://example.com#/$ref
Type : Static
Root : https://example.com
Schema : https://example.com
Pointer : /$ref
Base : https://example.com
Base URI : https://example.com
Relative Pointer : /$ref
Dialect : https://json-schema.org/draft/2020-12/schema
(POINTER) URI: https://example.com#/$schema
Type : Static
Root : https://example.com
Schema : https://example.com
Pointer : /$schema
Base : https://example.com
Base URI : https://example.com
Relative Pointer : /$schema
Dialect : https://json-schema.org/draft/2020-12/schema
(REFERENCE) URI: /$ref
Expand Down
Loading

0 comments on commit f4963d4

Please sign in to comment.