Skip to content

Commit 22820a3

Browse files
authored
Fix nested documentation subtables in patternProperties (#749)
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
1 parent 17342fa commit 22820a3

3 files changed

Lines changed: 1179 additions & 9 deletions

File tree

src/documentation/documentation.cc

Lines changed: 160 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,18 @@ auto walk_schema(const sourcemeta::core::JSON &schema, bool include_root,
513513
const sourcemeta::core::JSON &root, VisitedSchemas &visited,
514514
std::size_t &next_identifier) -> sourcemeta::core::JSON;
515515

516+
auto is_complex_schema(const sourcemeta::core::JSON &schema) -> bool;
517+
518+
auto walk_branching_subschema(const std::string &label,
519+
const std::string &synthetic_name,
520+
const sourcemeta::core::JSON &inner_schema,
521+
sourcemeta::core::JSON &doc_children,
522+
const sourcemeta::core::SchemaFrame &frame,
523+
const sourcemeta::core::JSON &root,
524+
VisitedSchemas &visited,
525+
std::size_t &next_identifier,
526+
bool include_properties) -> void;
527+
516528
auto walk_branches(const std::string &keyword, const std::string &label,
517529
const sourcemeta::core::JSON &schema,
518530
sourcemeta::core::JSON &children,
@@ -574,8 +586,9 @@ auto resolve_ref(const sourcemeta::core::JSON &schema,
574586
auto emit_row(const sourcemeta::core::JSON &schema, sourcemeta::core::JSON path,
575587
sourcemeta::core::JSON &rows,
576588
const sourcemeta::core::SchemaFrame &frame,
577-
const sourcemeta::core::JSON &root, const VisitedSchemas &visited,
578-
std::size_t &next_identifier) -> void {
589+
const sourcemeta::core::JSON &root, VisitedSchemas &visited,
590+
std::size_t &next_identifier,
591+
const bool expand_applicators = true) -> void {
579592
auto row{sourcemeta::core::JSON::make_object()};
580593
row.assign("identifier", sourcemeta::core::JSON{
581594
static_cast<std::int64_t>(next_identifier++)});
@@ -619,6 +632,34 @@ auto emit_row(const sourcemeta::core::JSON &schema, sourcemeta::core::JSON path,
619632
}
620633
}
621634

635+
if (expand_applicators && is_complex_schema(schema)) {
636+
auto row_children{sourcemeta::core::JSON::make_array()};
637+
walk_branches("anyOf", "Any of", schema, row_children, frame, root, visited,
638+
next_identifier);
639+
walk_branches("oneOf", "One of", schema, row_children, frame, root, visited,
640+
next_identifier);
641+
walk_all_of(schema, rows, row_children, frame, root, visited,
642+
next_identifier);
643+
walk_if_then_else(schema, row_children, frame, root, visited,
644+
next_identifier);
645+
if (schema.is_object() && schema.defines("not")) {
646+
const auto &not_schema{schema.at("not")};
647+
const auto has_inline{
648+
not_schema.is_object() &&
649+
!(not_schema.defines("anyOf") || not_schema.defines("oneOf") ||
650+
not_schema.defines("allOf") || not_schema.defines("not")) &&
651+
!constraints_of(not_schema).empty()};
652+
if (!has_inline) {
653+
walk_branching_subschema("Must NOT match", "value", not_schema,
654+
row_children, frame, root, visited,
655+
next_identifier, false);
656+
}
657+
}
658+
if (!row_children.empty()) {
659+
row.assign("children", std::move(row_children));
660+
}
661+
}
662+
622663
rows.push_back(std::move(row));
623664
}
624665

@@ -688,6 +729,35 @@ auto walk_properties(const sourcemeta::core::JSON &schema,
688729

689730
const auto row_identifier{
690731
static_cast<std::size_t>(row.at("identifier").to_integer())};
732+
733+
if (is_complex_schema(resolved)) {
734+
auto prop_children{sourcemeta::core::JSON::make_array()};
735+
walk_branches("anyOf", "Any of", resolved, prop_children, frame, root,
736+
visited, next_identifier);
737+
walk_branches("oneOf", "One of", resolved, prop_children, frame, root,
738+
visited, next_identifier);
739+
walk_all_of(resolved, rows, prop_children, frame, root, visited,
740+
next_identifier);
741+
walk_if_then_else(resolved, prop_children, frame, root, visited,
742+
next_identifier);
743+
if (resolved.defines("not")) {
744+
const auto &not_schema{resolved.at("not")};
745+
const auto has_inline{
746+
not_schema.is_object() &&
747+
!(not_schema.defines("anyOf") || not_schema.defines("oneOf") ||
748+
not_schema.defines("allOf") || not_schema.defines("not")) &&
749+
!constraints_of(not_schema).empty()};
750+
if (!has_inline) {
751+
walk_branching_subschema("Must NOT match", "value", not_schema,
752+
prop_children, frame, root, visited,
753+
next_identifier, false);
754+
}
755+
}
756+
if (!prop_children.empty()) {
757+
row.assign("children", std::move(prop_children));
758+
}
759+
}
760+
691761
rows.push_back(std::move(row));
692762

693763
if (resolved.is_object() && resolved.defines("type") &&
@@ -786,7 +856,63 @@ auto walk_wildcard_keyword(const sourcemeta::core::JSON &schema,
786856

787857
auto path{base_path};
788858
path.push_back(make_path_segment("wildcard", "*"));
789-
emit_row(value, std::move(path), rows, frame, root, visited, next_identifier);
859+
const auto wildcard_row_id{next_identifier};
860+
emit_row(value, path, rows, frame, root, visited, next_identifier);
861+
862+
if (value.defines("type") && value.at("type").is_string() &&
863+
value.at("type").to_string() == "object") {
864+
visited.emplace(&value,
865+
VisitedEntry{.identifier = wildcard_row_id, .path = path});
866+
walk_properties(value, path, rows, frame, root, visited, next_identifier);
867+
walk_pattern_properties(value, path, rows, frame, root, visited,
868+
next_identifier);
869+
walk_wildcard_keyword(value, "additionalProperties", path, rows, frame,
870+
root, visited, next_identifier);
871+
walk_wildcard_keyword(value, "unevaluatedProperties", path, rows, frame,
872+
root, visited, next_identifier);
873+
if (!value.defines("additionalProperties") &&
874+
!value.defines("unevaluatedProperties")) {
875+
auto open_path{path};
876+
open_path.push_back(make_path_segment("wildcard", "*"));
877+
emit_row(sourcemeta::core::JSON{true}, std::move(open_path), rows, frame,
878+
root, visited, next_identifier);
879+
}
880+
visited.erase(&value);
881+
} else if (value.defines("type") && value.at("type").is_string() &&
882+
value.at("type").to_string() == "array" &&
883+
value.defines("items") && value.at("items").is_object() &&
884+
!value.defines("prefixItems")) {
885+
const auto &items_schema{
886+
resolve_ref(value.at("items"), frame, root, visited)};
887+
if (items_schema.is_object()) {
888+
auto items_path{path};
889+
items_path.push_back(make_path_segment("wildcard", "*"));
890+
const auto items_row_id{next_identifier};
891+
emit_row(items_schema, items_path, rows, frame, root, visited,
892+
next_identifier);
893+
if (items_schema.defines("type") && items_schema.at("type").is_string() &&
894+
items_schema.at("type").to_string() == "object") {
895+
visited.emplace(&items_schema, VisitedEntry{.identifier = items_row_id,
896+
.path = items_path});
897+
walk_properties(items_schema, items_path, rows, frame, root, visited,
898+
next_identifier);
899+
walk_pattern_properties(items_schema, items_path, rows, frame, root,
900+
visited, next_identifier);
901+
walk_wildcard_keyword(items_schema, "additionalProperties", items_path,
902+
rows, frame, root, visited, next_identifier);
903+
walk_wildcard_keyword(items_schema, "unevaluatedProperties", items_path,
904+
rows, frame, root, visited, next_identifier);
905+
if (!items_schema.defines("additionalProperties") &&
906+
!items_schema.defines("unevaluatedProperties")) {
907+
auto open_path{items_path};
908+
open_path.push_back(make_path_segment("wildcard", "*"));
909+
emit_row(sourcemeta::core::JSON{true}, std::move(open_path), rows,
910+
frame, root, visited, next_identifier);
911+
}
912+
visited.erase(&items_schema);
913+
}
914+
}
915+
}
790916
}
791917

792918
auto walk_pattern_properties(const sourcemeta::core::JSON &schema,
@@ -802,10 +928,35 @@ auto walk_pattern_properties(const sourcemeta::core::JSON &schema,
802928
}
803929

804930
for (const auto &entry : schema.at("patternProperties").as_object()) {
931+
const auto &resolved{resolve_ref(entry.second, frame, root, visited)};
805932
auto path{base_path};
806933
path.push_back(make_path_segment("pattern", entry.first));
807-
emit_row(entry.second, std::move(path), rows, frame, root, visited,
808-
next_identifier);
934+
935+
const auto row_id{next_identifier};
936+
emit_row(resolved, path, rows, frame, root, visited, next_identifier);
937+
938+
if (resolved.is_object() && resolved.defines("type") &&
939+
resolved.at("type").is_string() &&
940+
resolved.at("type").to_string() == "object") {
941+
visited.emplace(&resolved,
942+
VisitedEntry{.identifier = row_id, .path = path});
943+
walk_properties(resolved, path, rows, frame, root, visited,
944+
next_identifier);
945+
walk_pattern_properties(resolved, path, rows, frame, root, visited,
946+
next_identifier);
947+
walk_wildcard_keyword(resolved, "additionalProperties", path, rows, frame,
948+
root, visited, next_identifier);
949+
walk_wildcard_keyword(resolved, "unevaluatedProperties", path, rows,
950+
frame, root, visited, next_identifier);
951+
if (!resolved.defines("additionalProperties") &&
952+
!resolved.defines("unevaluatedProperties")) {
953+
auto open_path{path};
954+
open_path.push_back(make_path_segment("wildcard", "*"));
955+
emit_row(sourcemeta::core::JSON{true}, std::move(open_path), rows,
956+
frame, root, visited, next_identifier);
957+
}
958+
visited.erase(&resolved);
959+
}
809960
}
810961
}
811962

@@ -1120,7 +1271,7 @@ auto walk_branching_subschema(const std::string &label,
11201271
visited, next_identifier);
11211272
}
11221273
emit_row(inner_schema, std::move(synthetic_path), table_rows, frame, root,
1123-
visited, next_identifier);
1274+
visited, next_identifier, false);
11241275
walk_branches("anyOf", "Any of", inner_schema, table_children, frame, root,
11251276
visited, next_identifier);
11261277
walk_branches("oneOf", "One of", inner_schema, table_children, frame, root,
@@ -1204,7 +1355,7 @@ auto walk_schema(const sourcemeta::core::JSON &schema, const bool include_root,
12041355
auto root_path{sourcemeta::core::JSON::make_array()};
12051356
root_path.push_back(make_path_segment("synthetic", "root"));
12061357
emit_row(schema, std::move(root_path), rows, frame, root, visited,
1207-
next_identifier);
1358+
next_identifier, false);
12081359
const auto root_row_identifier{static_cast<std::size_t>(
12091360
rows.at(rows.size() - 1).at("identifier").to_integer())};
12101361
auto visited_root_path{sourcemeta::core::JSON::make_array()};
@@ -1219,7 +1370,7 @@ auto walk_schema(const sourcemeta::core::JSON &schema, const bool include_root,
12191370
auto root_path{sourcemeta::core::JSON::make_array()};
12201371
root_path.push_back(make_path_segment("synthetic", "root"));
12211372
emit_row(schema, std::move(root_path), rows, frame, root, visited,
1222-
next_identifier);
1373+
next_identifier, false);
12231374
}
12241375
documentation.assign("rows", std::move(rows));
12251376
return documentation;
@@ -1229,7 +1380,7 @@ auto walk_schema(const sourcemeta::core::JSON &schema, const bool include_root,
12291380
auto root_path{sourcemeta::core::JSON::make_array()};
12301381
root_path.push_back(make_path_segment("synthetic", "root"));
12311382
emit_row(schema, std::move(root_path), rows, frame, root, visited,
1232-
next_identifier);
1383+
next_identifier, false);
12331384
}
12341385

12351386
const auto empty_path{sourcemeta::core::JSON::make_array()};

0 commit comments

Comments
 (0)