|
@@ -371,7 +371,12 @@ StoredNode StoredNode::parseFromCode(const std::string& code, const std::string&
|
|
|
// literal, or text jsLiteralToJson produced that nlohmann::json rejects -
|
|
// literal, or text jsLiteralToJson produced that nlohmann::json rejects -
|
|
|
// is recorded in node.schema_parse_errors so migration can refuse to
|
|
// is recorded in node.schema_parse_errors so migration can refuse to
|
|
|
// store a degraded node instead of silently writing null.
|
|
// store a degraded node instead of silently writing null.
|
|
|
- auto parse_schema_field = [&](const char* field_name, nlohmann::json& out) -> std::string {
|
|
|
|
|
|
|
+ // raw_json, when given, receives the exact JSON text that was parsed for
|
|
|
|
|
+ // `out` - so a caller that needs the same text again (uiOrder below)
|
|
|
|
|
+ // reuses it instead of re-running the regex search and jsLiteralToJson
|
|
|
|
|
+ // from scratch.
|
|
|
|
|
+ auto parse_schema_field = [&](const char* field_name, nlohmann::json& out,
|
|
|
|
|
+ std::string* raw_json = nullptr) -> std::string {
|
|
|
std::regex start_regex(std::string(R"(const\s+)") + field_name + R"(\s*=\s*)");
|
|
std::regex start_regex(std::string(R"(const\s+)") + field_name + R"(\s*=\s*)");
|
|
|
std::smatch field_match;
|
|
std::smatch field_match;
|
|
|
if (!std::regex_search(code, field_match, start_regex)) {
|
|
if (!std::regex_search(code, field_match, start_regex)) {
|
|
@@ -396,11 +401,15 @@ StoredNode StoredNode::parseFromCode(const std::string& code, const std::string&
|
|
|
LOG_WARN("Failed to parse {} for {}: {}", field_name, node.id, e.what());
|
|
LOG_WARN("Failed to parse {} for {}: {}", field_name, node.id, e.what());
|
|
|
return e.what();
|
|
return e.what();
|
|
|
}
|
|
}
|
|
|
|
|
+ if (raw_json) {
|
|
|
|
|
+ *raw_json = std::move(jsonStr);
|
|
|
|
|
+ }
|
|
|
return "";
|
|
return "";
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
{
|
|
{
|
|
|
- std::string err = parse_schema_field("configSchema", node.config_schema);
|
|
|
|
|
|
|
+ std::string config_json_str;
|
|
|
|
|
+ std::string err = parse_schema_field("configSchema", node.config_schema, &config_json_str);
|
|
|
if (!err.empty()) {
|
|
if (!err.empty()) {
|
|
|
node.schema_parse_errors.push_back("configSchema - " + err);
|
|
node.schema_parse_errors.push_back("configSchema - " + err);
|
|
|
} else if (!node.config_schema.is_null()) {
|
|
} else if (!node.config_schema.is_null()) {
|
|
@@ -409,30 +418,34 @@ StoredNode StoredNode::parseFromCode(const std::string& code, const std::string&
|
|
|
// nlohmann::json sorts an object's keys, so by the time a
|
|
// nlohmann::json sorts an object's keys, so by the time a
|
|
|
// schema reaches the editor "width" has drifted to the end
|
|
// schema reaches the editor "width" has drifted to the end
|
|
|
// and "height" near the front, whatever the node author
|
|
// and "height" near the front, whatever the node author
|
|
|
- // wrote. ordered_json keeps insertion order, so parsing a
|
|
|
|
|
- // second time with it recovers the real order.
|
|
|
|
|
|
|
+ // wrote. ordered_json keeps insertion order, so parsing the
|
|
|
|
|
+ // same JSON text again with it recovers the real order.
|
|
|
//
|
|
//
|
|
|
// It is stored as an ARRAY rather than by keeping the whole
|
|
// It is stored as an ARRAY rather than by keeping the whole
|
|
|
// schema ordered, because the schema is written to the
|
|
// schema ordered, because the schema is written to the
|
|
|
// database and read back, and nothing here controls whether
|
|
// database and read back, and nothing here controls whether
|
|
|
// that round trip preserves an object's key order. An array
|
|
// that round trip preserves an object's key order. An array
|
|
|
// has an order by definition and survives any of it.
|
|
// has an order by definition and survives any of it.
|
|
|
- std::regex config_start_regex(R"(const\s+configSchema\s*=\s*)");
|
|
|
|
|
- if (std::regex_search(code, match, config_start_regex)) {
|
|
|
|
|
- size_t start_pos = match.position() + match.length();
|
|
|
|
|
- size_t brace_pos = code.find('{', start_pos);
|
|
|
|
|
- if (brace_pos != std::string::npos) {
|
|
|
|
|
- std::string jsonStr = jsLiteralToJson(code, brace_pos);
|
|
|
|
|
- auto ordered = nlohmann::ordered_json::parse(jsonStr);
|
|
|
|
|
- if (ordered.contains("properties") && ordered["properties"].is_object()) {
|
|
|
|
|
- auto ui_order = nlohmann::json::array();
|
|
|
|
|
- for (auto it = ordered["properties"].begin();
|
|
|
|
|
- it != ordered["properties"].end(); ++it) {
|
|
|
|
|
- ui_order.push_back(it.key());
|
|
|
|
|
- }
|
|
|
|
|
- node.config_schema["uiOrder"] = ui_order;
|
|
|
|
|
|
|
+ //
|
|
|
|
|
+ // config_json_str already parsed cleanly above (that is what
|
|
|
|
|
+ // made err empty), so this reparse of the identical text is not
|
|
|
|
|
+ // expected to fail - but nlohmann::ordered_json::parse still
|
|
|
|
|
+ // throws on malformed input like any other json::parse, so it
|
|
|
|
|
+ // stays behind a try/catch rather than trust that a second
|
|
|
|
|
+ // parser agrees with the first.
|
|
|
|
|
+ try {
|
|
|
|
|
+ auto ordered = nlohmann::ordered_json::parse(config_json_str);
|
|
|
|
|
+ if (ordered.contains("properties") && ordered["properties"].is_object()) {
|
|
|
|
|
+ auto ui_order = nlohmann::json::array();
|
|
|
|
|
+ for (auto it = ordered["properties"].begin();
|
|
|
|
|
+ it != ordered["properties"].end(); ++it) {
|
|
|
|
|
+ ui_order.push_back(it.key());
|
|
|
}
|
|
}
|
|
|
|
|
+ node.config_schema["uiOrder"] = ui_order;
|
|
|
}
|
|
}
|
|
|
|
|
+ } catch (const nlohmann::json::exception& e) {
|
|
|
|
|
+ LOG_WARN("Failed to recover configSchema property order for {}: {}",
|
|
|
|
|
+ node.id, e.what());
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|