Browse Source

fix: add missing pointer functions

test: missed edge cases
Sam Jaffe 2 years ago
parent
commit
37078d6813
2 changed files with 114 additions and 3 deletions
  1. 24 0
      include/serializer/jsonizer.tpp
  2. 90 3
      test/jsonizer_test.cxx

+ 24 - 0
include/serializer/jsonizer.tpp

@@ -144,6 +144,30 @@ void Jsonizer::from_json(T & value, Json::Value const & json) const {
   }
 }
 
+template <typename T, typename F>
+T Jsonizer::from_cached_json(std::string const & key, Json::Value const & json,
+                             F && fetch) const {
+  std::shared_ptr<T const> ptr = p_cache->fetch<T>(json[key].asString());
+  if (!ptr) {
+    T value{};
+    std::forward<F>(fetch)(value, json);
+    ptr = p_cache->store(value.name, std::move(value));
+  }
+  return *ptr;
+}
+
+template <typename T>
+void Jsonizer::from_json(std::shared_ptr<T const> & ptr,
+                         Json::Value const & json) const {
+  if (json.isString()) {
+    ptr = p_cache->fetch<T>(json.asString());
+  } else {
+    T local{};
+    from_json(local, json);
+    ptr = p_cache->store(local.name, local);
+  }
+}
+
 template <typename T> T Jsonizer::from_json(Json::Value const & json) const {
   std::decay_t<T> tmp;
   from_json(tmp, json);

+ 90 - 3
test/jsonizer_test.cxx

@@ -25,6 +25,11 @@ using std::operator""s;
 using std::operator""sv;
 using namespace magic_enum::bitwise_operators;
 
+using testing::Field;
+using testing::IsNull;
+using testing::NotNull;
+using testing::Pointee;
+
 enum class Color { RED, BLUE, GREEN };
 enum class Mask { LEFT = 1, RIGHT = 2 };
 
@@ -72,12 +77,94 @@ TEST(JsonizerTest, ParsesCollection) {
   EXPECT_ROUNDTRIP(izer, std::set({1, 2, 3, 1}), "[1, 2, 3]"_json);
 }
 
+TEST(JsonizerTest, ThrowsOnParsingCollectionFromNonArray) {
+  serializer::Jsonizer izer;
+  std::vector<int> out;
+  EXPECT_THROW(izer.from_json(out, "1"_json), std::invalid_argument);
+}
+
 TEST(JsonizerTest, ParsesAssociative) {
   serializer::Jsonizer izer;
   EXPECT_ROUNDTRIP(izer, (std::map<int, int>{{1, 2}, {3, 1}}),
                    "{\"1\":2, \"3\":1}"_json);
+}
+
+TEST(JsonizerTest, AssociativeCanParseFromPairListOrObject) {
+  using Type = std::map<std::string, int>;
+  serializer::Jsonizer izer;
+
+  EXPECT_THAT(izer.from_json<Type>("[[\"A\", 2], [\"B\", 1]]"_json),
+              (Type{{"A", 2}, {"B", 1}}));
+
+  EXPECT_THAT(izer.from_json<Type>("{ \"A\": 2, \"B\": 1 }"_json),
+              (Type{{"A", 2}, {"B", 1}}));
+}
+
+TEST(JsonizerTest, AssociativeWithNonTrivialKeyIsPairList) {
+  using Type = std::map<std::pair<int, int>, int>;
+  serializer::Jsonizer izer;
+  EXPECT_ROUNDTRIP(izer, (Type{{{1, 2}, 1}, {{2, 1}, 2}}),
+                   "[[[1, 2], 1], [[2, 1], 2]]"_json);
+}
+
+TEST(JsonizerTest, ThrowsOnParsingAssociativeFromNonCollection) {
+  serializer::Jsonizer izer;
+  std::map<int, int> out;
+  EXPECT_THROW(izer.from_json(out, "1"_json), std::invalid_argument);
+}
+
+struct Cacheable {
+  std::string name;
+};
 
-  std::map<int, int> ex;
-  izer.from_json(ex, "[[1, 2], [3, 1]]"_json);
-  EXPECT_THAT(ex, (std::map<int, int>{{1, 2}, {3, 1}}));
+template <>
+void serializer::Jsonizer::from_json(Cacheable & value,
+                                     Json::Value const & json) const {
+  from_json(value.name, json["name"]);
+}
+
+TEST(JsonizerTest, CanAttemptToLoadFromCache) {
+  serializer::Jsonizer izer;
+  std::shared_ptr<Cacheable const> ptr;
+
+  EXPECT_THROW(izer.from_json(ptr, "\"ONE\""_json), std::domain_error);
+  EXPECT_THAT(ptr, IsNull());
+
+  EXPECT_NO_THROW(izer.from_json(ptr, "{\"name\":\"ONE\"}"_json));
+  EXPECT_THAT(ptr, NotNull());
+  EXPECT_THAT(ptr, Pointee(Field(&Cacheable::name, "ONE")));
+}
+
+struct Serializable {
+  using serial_type = std::map<int, int>;
+
+  Serializable(std::vector<int> value = {}) : value(value) {}
+
+  Serializable(serial_type vals) {
+    for (auto [k, v] : vals) {
+      while (v--) {
+        value.push_back(k);
+      }
+    }
+  }
+
+  operator serial_type() const {
+    serial_type rval;
+    for (auto v : value) {
+      ++rval[v];
+    }
+    return rval;
+  }
+
+  std::vector<int> value;
+};
+
+bool operator==(Serializable const & lhs, Serializable const & rhs) {
+  return lhs.value == rhs.value;
+}
+
+TEST(JsonizerTest, CanConstructFromAltType) {
+  serializer::Jsonizer izer;
+  EXPECT_ROUNDTRIP(izer, Serializable({1, 1, 1, 1, 2, 2, 2, 3, 3, 4}),
+                   "{\"1\":4, \"2\":3, \"3\":2, \"4\":1}"_json);
 }