From a06e7f5d80af9b5a97f4a3ddcc25dd97c0b1d9dc Mon Sep 17 00:00:00 2001
From: Patrick Boettcher <p@yai.se>
Date: Thu, 24 Jan 2019 16:46:51 +0100
Subject: [PATCH] JSON-pointer: add operator+() returning a new json_pointer

---
 include/nlohmann/detail/json_pointer.hpp | 23 +++++++++-
 single_include/nlohmann/json.hpp         | 23 +++++++++-
 test/src/unit-json_pointer.cpp           | 54 ++++++++++++++++++++++++
 3 files changed, 96 insertions(+), 4 deletions(-)

diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp
index 033625bc..705346c5 100644
--- a/include/nlohmann/detail/json_pointer.hpp
+++ b/include/nlohmann/detail/json_pointer.hpp
@@ -114,14 +114,33 @@ class json_pointer
     }
 
     /*!
-    @brief remove and return last reference pointer
-    @throw out_of_range.405 if JSON pointer has no parent
+    @brief append a token at the end of the reference pointer
     */
     void push_back(const std::string& tok)
     {
         reference_tokens.push_back(tok);
     }
 
+    /*!
+    @brief append a key-token at the end of the reference pointer and return a new json-pointer.
+    */
+    json_pointer operator+(const std::string& tok) const
+    {
+        auto ptr = *this;
+        ptr.push_back(tok);
+        return ptr;
+    }
+
+    /*!
+    @brief append a array-index-token at the end of the reference pointer and return a new json-pointer.
+    */
+    json_pointer operator+(const size_t& index) const
+    {
+        auto ptr = *this;
+        ptr.push_back(std::to_string(index));
+        return ptr;
+    }
+
   private:
     /// return whether pointer points to the root document
     bool is_root() const noexcept
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index ee72531b..d845f77e 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -11924,14 +11924,33 @@ class json_pointer
     }
 
     /*!
-    @brief remove and return last reference pointer
-    @throw out_of_range.405 if JSON pointer has no parent
+    @brief append a token at the end of the reference pointer
     */
     void push_back(const std::string& tok)
     {
         reference_tokens.push_back(tok);
     }
 
+    /*!
+    @brief append a key-token at the end of the reference pointer and return a new json-pointer.
+    */
+    json_pointer operator+(const std::string& tok) const
+    {
+        auto ptr = *this;
+        ptr.push_back(tok);
+        return ptr;
+    }
+
+    /*!
+    @brief append a array-index-token at the end of the reference pointer and return a new json-pointer.
+    */
+    json_pointer operator+(const size_t& index) const
+    {
+        auto ptr = *this;
+        ptr.push_back(std::to_string(index));
+        return ptr;
+    }
+
   private:
     /// return whether pointer points to the root document
     bool is_root() const noexcept
diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp
index 1bcd3938..373b2c21 100644
--- a/test/src/unit-json_pointer.cpp
+++ b/test/src/unit-json_pointer.cpp
@@ -513,4 +513,58 @@ TEST_CASE("JSON pointers")
         CHECK(j[ptr] == j["object"]["/"]);
         CHECK(ptr.to_string() == "/object/~1");
     }
+
+    SECTION("operators")
+    {
+        const json j =
+        {
+            {"", "Hello"},
+            {"pi", 3.141},
+            {"happy", true},
+            {"name", "Niels"},
+            {"nothing", nullptr},
+            {
+                "answer", {
+                    {"everything", 42}
+                }
+            },
+            {"list", {1, 0, 2}},
+            {
+                "object", {
+                    {"currency", "USD"},
+                    {"value", 42.99},
+                    {"", "empty string"},
+                    {"/", "slash"},
+                    {"~", "tilde"},
+                    {"~1", "tilde1"}
+                }
+            }
+        };
+
+        // empty json_pointer returns the root JSON-object
+        auto ptr = ""_json_pointer;
+        CHECK(j[ptr] == j);
+
+        // simple field access
+        ptr = ptr + "pi";
+        CHECK(j[ptr] == j["pi"]);
+
+        ptr.pop_back();
+        CHECK(j[ptr] == j);
+
+        // object and children access
+        ptr = ptr + "answer";
+        ptr = ptr + "everything";
+        CHECK(j[ptr] == j["answer"]["everything"]);
+
+        ptr.pop_back();
+        ptr.pop_back();
+        CHECK(j[ptr] == j);
+
+        // push key which has to be encoded
+        ptr = ptr + "object";
+        ptr = ptr + "/";
+        CHECK(j[ptr] == j["object"]["/"]);
+        CHECK(ptr.to_string() == "/object/~1");
+    }
 }