|
4 | 4 | #include <filesystem> // C++17 |
5 | 5 | #include <sstream> |
6 | 6 | #include <algorithm> |
| 7 | +#include <fstream> |
7 | 8 | //////////////////////////////////////////////////////////////////////////////// |
8 | 9 |
|
9 | 10 | namespace jse |
@@ -46,19 +47,111 @@ namespace jse |
46 | 47 |
|
47 | 48 | namespace |
48 | 49 | { |
| 50 | + // adds key after the provided pointer, correctly handling the json special case that root has an ending / |
49 | 51 | std::string append_pointer(const std::string &pointer, const std::string &key) |
50 | 52 | { |
51 | 53 | if (pointer == "/") |
52 | 54 | return "/" + key; |
53 | 55 | else |
54 | 56 | return pointer + "/" + key; |
55 | 57 | } |
| 58 | + |
| 59 | + // adds key before the provided pointer, correctly handling the json special case that root has an ending / |
| 60 | + std::string prepend_pointer(const std::string &pointer, const std::string &key) |
| 61 | + { |
| 62 | + if (key == "/") |
| 63 | + return pointer; |
| 64 | + else |
| 65 | + return key + "/" + pointer; |
| 66 | + } |
| 67 | + |
56 | 68 | } // namespace |
57 | 69 |
|
| 70 | + |
| 71 | + // enriches a given json spec with included json specs |
| 72 | + json JSE::inject_include(const json &rules) |
| 73 | + { |
| 74 | + std::vector<string> dirs = include_directories; |
| 75 | + dirs.push_back(""); // adding default path |
| 76 | + |
| 77 | + // max 10 levels of nesting to avoid infinite loops |
| 78 | + json current = rules; |
| 79 | + |
| 80 | + for (size_t x = 0; x < 10; x++) |
| 81 | + { |
| 82 | + // check if the rules have any include |
| 83 | + bool include_present = false; |
| 84 | + for (auto rule : current) |
| 85 | + if (rule.at("type") == "include") |
| 86 | + include_present = true; |
| 87 | + |
| 88 | + // if there are no includes, return the current ones |
| 89 | + if (!include_present) |
| 90 | + return current; |
| 91 | + |
| 92 | + json enriched; |
| 93 | + // otherwise, do a round of replacement |
| 94 | + for (auto rule : current) |
| 95 | + { |
| 96 | + // copy all rules that are not include |
| 97 | + if (rule.at("type") != "include") |
| 98 | + { |
| 99 | + enriched.push_back(rule); |
| 100 | + } |
| 101 | + // if the rule is an include, expand the node with a copy of the included file |
| 102 | + else |
| 103 | + { |
| 104 | + bool replaced = false; |
| 105 | + // the include file could be in any of the include directories |
| 106 | + for (auto dir : dirs) |
| 107 | + { |
| 108 | + string spec_file = rule.at("spec_file"); |
| 109 | + string f = dir + spec_file; |
| 110 | + // check if the file exists |
| 111 | + if (std::filesystem::is_regular_file(f)) |
| 112 | + { |
| 113 | + std::ifstream ifs(f); |
| 114 | + json include_rules = json::parse(ifs); |
| 115 | + |
| 116 | + // loop over all rules to add the prefix |
| 117 | + for (auto i_rule : include_rules) |
| 118 | + { |
| 119 | + string prefix = rule.at("pointer"); |
| 120 | + string pointer = i_rule.at("pointer"); |
| 121 | + i_rule.at("pointer") = prepend_pointer(pointer,prefix); |
| 122 | + } |
| 123 | + |
| 124 | + // save modified rules |
| 125 | + for (auto i_rule : include_rules) |
| 126 | + enriched.push_back(i_rule); |
| 127 | + |
| 128 | + // one substitution is enough, give up the search over include dirs |
| 129 | + replaced = true; |
| 130 | + break; |
| 131 | + } |
| 132 | + |
| 133 | + } |
| 134 | + |
| 135 | + if (!replaced) |
| 136 | + { |
| 137 | + string pointer = rule.at("pointer"); |
| 138 | + throw("Failed to replace the include rule: " + pointer); |
| 139 | + assert(replaced == true); |
| 140 | + } |
| 141 | + } |
| 142 | + } |
| 143 | + |
| 144 | + // now that we replaced the include, copy it back to current |
| 145 | + current = enriched; |
| 146 | + } |
| 147 | + |
| 148 | + throw("Reached maximal 10 levels of include recursion."); |
| 149 | + |
| 150 | + } |
| 151 | + |
| 152 | + |
58 | 153 | bool JSE::verify_json(const string &pointer, json &input, const json &rules) |
59 | 154 | { |
60 | | - // if (pointer == "/common") |
61 | | - // std::cout << "gotcha" << std::endl; |
62 | 155 | // Find all rules that apply for the input node |
63 | 156 | // TODO: accelerate this |
64 | 157 | std::vector<json> matching_rules = collect_pointer(pointer, rules); |
@@ -216,6 +309,8 @@ namespace jse |
216 | 309 | return verify_rule_object(input, rule); |
217 | 310 | else if (type == "bool") |
218 | 311 | return verify_rule_bool(input, rule); |
| 312 | + else if (type == "include") |
| 313 | + return verify_rule_include(input, rule); |
219 | 314 | else |
220 | 315 | { |
221 | 316 | log.push_back(log_item("error", "Unknown rule type " + type)); |
@@ -362,6 +457,14 @@ namespace jse |
362 | 457 | return true; |
363 | 458 | } |
364 | 459 |
|
| 460 | + bool JSE::verify_rule_include(const json &input, const json &rule) |
| 461 | + { |
| 462 | + assert(rule.at("type") == "include"); |
| 463 | + |
| 464 | + // An include rule always fails, they should be processed first by the inject_include function |
| 465 | + return false; |
| 466 | + } |
| 467 | + |
365 | 468 | json JSE::collect_default_rules(const json &rules) |
366 | 469 | { |
367 | 470 | // Find all rules that apply for the input node |
|
0 commit comments