Explorar el Código

string(JSON): Add STRING_ENCODE mode

Kyle Edwards hace 2 meses
padre
commit
06e6f1e69f

+ 10 - 0
Help/command/string.rst

@@ -56,6 +56,8 @@ Synopsis
            <member|index> [<member|index> ...] <value>)
     string(JSON <out-var> [ERROR_VARIABLE <error-var>]
            `EQUAL <JSON-EQUAL_>`__ <json-string1> <json-string2>)
+    string(JSON <out-var> [ERROR_VARIABLE <error-var>]
+           `STRING_ENCODE <STRING-ENCODE_>`__ <string>)
 
 Search and Replace
 ^^^^^^^^^^^^^^^^^^
@@ -629,3 +631,11 @@ string is passed as a single argument even if it contains semicolons.
   and ``<json-string2>`` should be valid JSON.  The ``<out-var>``
   will be set to a true value if the JSON objects are considered equal,
   or a false value otherwise.
+
+.. signature::
+  string(JSON <out-var> [ERROR_VARIABLE <error-var>]
+         STRING_ENCODE <string>)
+  :target: STRING-ENCODE
+
+  Turn a raw string into a JSON string surrounded by quotes. Special characters
+  will be properly escaped inside the JSON string.

+ 1 - 1
Help/release/dev/string-json-improvements.rst

@@ -1,4 +1,4 @@
 string-json-improvements
 ------------------------
 
-* The :command:`string(JSON)` command gained a new ``GET_RAW`` mode.
+* The :command:`string(JSON)` command gained new ``GET_RAW`` and ``STRING_ENCODE`` modes.

+ 96 - 89
Source/cmStringCommand.cxx

@@ -954,113 +954,120 @@ bool HandleJSONCommand(std::vector<std::string> const& arguments,
     auto const& mode = args.PopFront("missing mode argument"_s);
     if (mode != "GET"_s && mode != "GET_RAW"_s && mode != "TYPE"_s &&
         mode != "MEMBER"_s && mode != "LENGTH"_s && mode != "REMOVE"_s &&
-        mode != "SET"_s && mode != "EQUAL"_s) {
+        mode != "SET"_s && mode != "EQUAL"_s && mode != "STRING_ENCODE"_s) {
       throw json_error(cmStrCat(
         "got an invalid mode '"_s, mode,
         "', expected one of GET, GET_RAW, TYPE, MEMBER, LENGTH, REMOVE, SET, "
-        " EQUAL"_s));
+        " EQUAL, STRING_ENCODE"_s));
     }
 
     auto const& jsonstr = args.PopFront("missing json string argument"_s);
-    Json::Value json = ReadJson(jsonstr);
 
-    if (mode == "GET"_s) {
-      auto const& value = ResolvePath(json, args);
-      if (value.isObject() || value.isArray()) {
-        makefile.AddDefinition(*outputVariable, WriteJson(value));
-      } else if (value.isBool()) {
-        makefile.AddDefinitionBool(*outputVariable, value.asBool());
-      } else {
-        makefile.AddDefinition(*outputVariable, value.asString());
-      }
+    if (mode == "STRING_ENCODE"_s) {
+      Json::Value json(jsonstr);
+      makefile.AddDefinition(*outputVariable, WriteJson(json));
+    } else {
+      Json::Value json = ReadJson(jsonstr);
+
+      if (mode == "GET"_s) {
+        auto const& value = ResolvePath(json, args);
+        if (value.isObject() || value.isArray()) {
+          makefile.AddDefinition(*outputVariable, WriteJson(value));
+        } else if (value.isBool()) {
+          makefile.AddDefinitionBool(*outputVariable, value.asBool());
+        } else {
+          makefile.AddDefinition(*outputVariable, value.asString());
+        }
 
-    } else if (mode == "GET_RAW"_s) {
-      auto const& value = ResolvePath(json, args);
-      makefile.AddDefinition(*outputVariable, WriteJson(value));
-
-    } else if (mode == "TYPE"_s) {
-      auto const& value = ResolvePath(json, args);
-      makefile.AddDefinition(*outputVariable, JsonTypeToString(value.type()));
-
-    } else if (mode == "MEMBER"_s) {
-      auto const& indexStr = args.PopBack("missing member index"_s);
-      auto const& value = ResolvePath(json, args);
-      if (!value.isObject()) {
-        throw json_error(
-          cmStrCat("MEMBER needs to be called with an element of "
-                   "type OBJECT, got "_s,
-                   JsonTypeToString(value.type())),
-          args);
-      }
-      auto const index = ParseIndex(
-        indexStr, Args{ args.begin(), args.end() + 1 }, value.size());
-      auto const memIt = std::next(value.begin(), index);
-      makefile.AddDefinition(*outputVariable, memIt.name());
-
-    } else if (mode == "LENGTH"_s) {
-      auto const& value = ResolvePath(json, args);
-      if (!value.isArray() && !value.isObject()) {
-        throw json_error(cmStrCat("LENGTH needs to be called with an "
-                                  "element of type ARRAY or OBJECT, got "_s,
-                                  JsonTypeToString(value.type())),
-                         args);
-      }
+      } else if (mode == "GET_RAW"_s) {
+        auto const& value = ResolvePath(json, args);
+        makefile.AddDefinition(*outputVariable, WriteJson(value));
 
-      cmAlphaNum sizeStr{ value.size() };
-      makefile.AddDefinition(*outputVariable, sizeStr.View());
+      } else if (mode == "TYPE"_s) {
+        auto const& value = ResolvePath(json, args);
+        makefile.AddDefinition(*outputVariable,
+                               JsonTypeToString(value.type()));
+
+      } else if (mode == "MEMBER"_s) {
+        auto const& indexStr = args.PopBack("missing member index"_s);
+        auto const& value = ResolvePath(json, args);
+        if (!value.isObject()) {
+          throw json_error(
+            cmStrCat("MEMBER needs to be called with an element of "
+                     "type OBJECT, got "_s,
+                     JsonTypeToString(value.type())),
+            args);
+        }
+        auto const index = ParseIndex(
+          indexStr, Args{ args.begin(), args.end() + 1 }, value.size());
+        auto const memIt = std::next(value.begin(), index);
+        makefile.AddDefinition(*outputVariable, memIt.name());
+
+      } else if (mode == "LENGTH"_s) {
+        auto const& value = ResolvePath(json, args);
+        if (!value.isArray() && !value.isObject()) {
+          throw json_error(cmStrCat("LENGTH needs to be called with an "
+                                    "element of type ARRAY or OBJECT, got "_s,
+                                    JsonTypeToString(value.type())),
+                           args);
+        }
 
-    } else if (mode == "REMOVE"_s) {
-      auto const& toRemove =
-        args.PopBack("missing member or index to remove"_s);
-      auto& value = ResolvePath(json, args);
+        cmAlphaNum sizeStr{ value.size() };
+        makefile.AddDefinition(*outputVariable, sizeStr.View());
 
-      if (value.isArray()) {
-        auto const index = ParseIndex(
-          toRemove, Args{ args.begin(), args.end() + 1 }, value.size());
-        Json::Value removed;
-        value.removeIndex(index, &removed);
+      } else if (mode == "REMOVE"_s) {
+        auto const& toRemove =
+          args.PopBack("missing member or index to remove"_s);
+        auto& value = ResolvePath(json, args);
 
-      } else if (value.isObject()) {
-        Json::Value removed;
-        value.removeMember(toRemove, &removed);
+        if (value.isArray()) {
+          auto const index = ParseIndex(
+            toRemove, Args{ args.begin(), args.end() + 1 }, value.size());
+          Json::Value removed;
+          value.removeIndex(index, &removed);
 
-      } else {
-        throw json_error(cmStrCat("REMOVE needs to be called with an "
-                                  "element of type ARRAY or OBJECT, got "_s,
-                                  JsonTypeToString(value.type())),
-                         args);
-      }
-      makefile.AddDefinition(*outputVariable, WriteJson(json));
+        } else if (value.isObject()) {
+          Json::Value removed;
+          value.removeMember(toRemove, &removed);
 
-    } else if (mode == "SET"_s) {
-      auto const& newValueStr = args.PopBack("missing new value remove"_s);
-      auto const& toAdd = args.PopBack("missing member name to add"_s);
-      auto& value = ResolvePath(json, args);
-
-      Json::Value newValue = ReadJson(newValueStr);
-      if (value.isObject()) {
-        value[toAdd] = newValue;
-      } else if (value.isArray()) {
-        auto const index =
-          ParseIndex(toAdd, Args{ args.begin(), args.end() + 1 });
-        if (value.isValidIndex(index)) {
-          value[static_cast<int>(index)] = newValue;
         } else {
-          value.append(newValue);
+          throw json_error(cmStrCat("REMOVE needs to be called with an "
+                                    "element of type ARRAY or OBJECT, got "_s,
+                                    JsonTypeToString(value.type())),
+                           args);
+        }
+        makefile.AddDefinition(*outputVariable, WriteJson(json));
+
+      } else if (mode == "SET"_s) {
+        auto const& newValueStr = args.PopBack("missing new value remove"_s);
+        auto const& toAdd = args.PopBack("missing member name to add"_s);
+        auto& value = ResolvePath(json, args);
+
+        Json::Value newValue = ReadJson(newValueStr);
+        if (value.isObject()) {
+          value[toAdd] = newValue;
+        } else if (value.isArray()) {
+          auto const index =
+            ParseIndex(toAdd, Args{ args.begin(), args.end() + 1 });
+          if (value.isValidIndex(index)) {
+            value[static_cast<int>(index)] = newValue;
+          } else {
+            value.append(newValue);
+          }
+        } else {
+          throw json_error(cmStrCat("SET needs to be called with an "
+                                    "element of type OBJECT or ARRAY, got "_s,
+                                    JsonTypeToString(value.type())));
         }
-      } else {
-        throw json_error(cmStrCat("SET needs to be called with an "
-                                  "element of type OBJECT or ARRAY, got "_s,
-                                  JsonTypeToString(value.type())));
-      }
 
-      makefile.AddDefinition(*outputVariable, WriteJson(json));
+        makefile.AddDefinition(*outputVariable, WriteJson(json));
 
-    } else if (mode == "EQUAL"_s) {
-      auto const& jsonstr2 =
-        args.PopFront("missing second json string argument"_s);
-      Json::Value json2 = ReadJson(jsonstr2);
-      makefile.AddDefinitionBool(*outputVariable, json == json2);
+      } else if (mode == "EQUAL"_s) {
+        auto const& jsonstr2 =
+          args.PopFront("missing second json string argument"_s);
+        Json::Value json2 = ReadJson(jsonstr2);
+        makefile.AddDefinitionBool(*outputVariable, json == json2);
+      }
     }
 
   } catch (json_error const& e) {

+ 16 - 0
Tests/RunCMake/string/JSON.cmake

@@ -360,3 +360,19 @@ assert_json_equal("${error}" "${result}"
   "foo" : "bar",
   "array" : [5, "val", {"some": "other"}, null, "append"]
 }]=])
+
+# Test STRING_ENCODE
+string(JSON result STRING_ENCODE Hello)
+assert_strequal("${result}" "\"Hello\"")
+string(JSON result STRING_ENCODE "\"Hello\"")
+assert_strequal("${result}" "\"\\\"Hello\\\"\"")
+string(JSON result STRING_ENCODE null)
+assert_strequal("${result}" "\"null\"")
+string(JSON result STRING_ENCODE 0)
+assert_strequal("${result}" "\"0\"")
+string(JSON result STRING_ENCODE false)
+assert_strequal("${result}" "\"false\"")
+string(JSON result STRING_ENCODE {})
+assert_strequal("${result}" "\"{}\"")
+string(JSON result STRING_ENCODE [])
+assert_strequal("${result}" "\"[]\"")

+ 1 - 1
Tests/RunCMake/string/JSONWrongMode-stderr.txt

@@ -1,5 +1,5 @@
 CMake Error at JSONWrongMode\.cmake:1 \(string\):
   string sub-command JSON got an invalid mode 'FOO', expected one of GET,
-  GET_RAW, TYPE, MEMBER, LENGTH, REMOVE, SET, EQUAL\.
+  GET_RAW, TYPE, MEMBER, LENGTH, REMOVE, SET, EQUAL, STRING_ENCODE\.
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)