Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/core/StarJsonPatch.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/core/StarJsonPatch.cpp')
-rw-r--r--source/core/StarJsonPatch.cpp97
1 files changed, 97 insertions, 0 deletions
diff --git a/source/core/StarJsonPatch.cpp b/source/core/StarJsonPatch.cpp
new file mode 100644
index 0000000..e1ab6de
--- /dev/null
+++ b/source/core/StarJsonPatch.cpp
@@ -0,0 +1,97 @@
+#include "StarJsonPatch.hpp"
+#include "StarJsonPath.hpp"
+#include "StarLexicalCast.hpp"
+
+namespace Star {
+
+Json jsonPatch(Json const& base, JsonArray const& patch) {
+ auto res = base;
+ try {
+ for (auto const& operation : patch) {
+ res = JsonPatching::applyOperation(res, operation);
+ }
+ return res;
+ } catch (JsonException const& e) {
+ throw JsonPatchException(strf("Could not apply patch to base. %s", e.what()));
+ }
+}
+
+namespace JsonPatching {
+
+ static const StringMap<std::function<Json(Json, Json)>> functionMap = StringMap<std::function<Json(Json, Json)>>{
+ {"test", std::bind(applyTestOperation, _1, _2)},
+ {"remove", std::bind(applyRemoveOperation, _1, _2)},
+ {"add", std::bind(applyAddOperation, _1, _2)},
+ {"replace", std::bind(applyReplaceOperation, _1, _2)},
+ {"move", std::bind(applyMoveOperation, _1, _2)},
+ {"copy", std::bind(applyCopyOperation, _1, _2)},
+ };
+
+ Json applyOperation(Json const& base, Json const& op) {
+ try {
+ auto operation = op.getString("op");
+ return JsonPatching::functionMap.get(operation)(base, op);
+ } catch (JsonException const& e) {
+ throw JsonPatchException(strf("Could not apply operation to base. %s", e.what()));
+ } catch (MapException const&) {
+ throw JsonPatchException(strf("Invalid operation: %s", op.getString("op")));
+ }
+ }
+
+ Json applyTestOperation(Json const& base, Json const& op) {
+ auto path = op.getString("path");
+ auto value = op.opt("value");
+ auto inverseTest = op.getBool("inverse", false);
+
+ auto pointer = JsonPath::Pointer(path);
+
+ try {
+ auto testValue = pointer.get(base);
+ if (!value) {
+ if (inverseTest)
+ throw JsonPatchTestFail(strf("Test operation failure, expected %s to be missing.", op.getString("path")));
+ return base;
+ }
+
+ if ((value && (testValue == *value)) ^ inverseTest) {
+ return base;
+ }
+
+ throw JsonPatchTestFail(strf("Test operation failure, expected %s found %s.", value, testValue));
+ } catch (JsonPath::TraversalException& e) {
+ if (inverseTest)
+ return base;
+ throw JsonPatchTestFail(strf("Test operation failure: %s", e.what()));
+ }
+ }
+
+ Json applyRemoveOperation(Json const& base, Json const& op) {
+ return JsonPath::Pointer(op.getString("path")).remove(base);
+ }
+
+ Json applyAddOperation(Json const& base, Json const& op) {
+ return JsonPath::Pointer(op.getString("path")).add(base, op.get("value"));
+ }
+
+ Json applyReplaceOperation(Json const& base, Json const& op) {
+ auto pointer = JsonPath::Pointer(op.getString("path"));
+ return pointer.add(pointer.remove(base), op.get("value"));
+ }
+
+ Json applyMoveOperation(Json const& base, Json const& op) {
+ auto fromPointer = JsonPath::Pointer(op.getString("from"));
+ auto toPointer = JsonPath::Pointer(op.getString("path"));
+
+ Json value = fromPointer.get(base);
+ return toPointer.add(fromPointer.remove(base), value);
+ }
+
+ Json applyCopyOperation(Json const& base, Json const& op) {
+ auto fromPointer = JsonPath::Pointer(op.getString("from"));
+ auto toPointer = JsonPath::Pointer(op.getString("path"));
+
+ return toPointer.add(base, fromPointer.get(base));
+ }
+}
+
+}