diff --git a/.nxxm/deps b/.nxxm/deps deleted file mode 100644 index 3f82acdd..00000000 --- a/.nxxm/deps +++ /dev/null @@ -1,4 +0,0 @@ -{ - "nlohmann/json" : { "@" : "v3.1.2", "x" : ["benchmarks"] } - , "platform" : [ "Boost::+boost" ] -} diff --git a/.tipi/deps b/.tipi/deps new file mode 100644 index 00000000..8c109b3e --- /dev/null +++ b/.tipi/deps @@ -0,0 +1,6 @@ +{ + "nlohmann/json" : { "@" : "v3.1.2", "x" : ["benchmarks"] } + , "boostorg/pfr" : {} + , "tipi-build/constexpr_all_the_things" : { "@" : "feature/json-substrings-as-types" } + , "platform" : [ "Boost::+boost" ] +} diff --git a/pre/cx/key_value_pair.hpp b/pre/cx/key_value_pair.hpp new file mode 100644 index 00000000..42994f54 --- /dev/null +++ b/pre/cx/key_value_pair.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include +#include + +#define pre_json_key(type, name) ::pre::cx::key_value_pair<"" # name, type> name + +namespace pre::cx { + using ::cx::static_string; + template + struct tstring { + static constexpr cx::static_string value = str; + }; + + template + struct key_value_pair { + using key = tstring; + T value; + friend bool operator<(const key_value_pair& x, const key_value_pair& y) { + return x.value < y.value; + } + template friend bool operator==(const key_value_pair& lhs, const U& rhs){return lhs.value == rhs.value;} + template friend bool operator< (const key_value_pair& lhs, const U& rhs){return lhs.value < rhs.value;} + template friend bool operator!=(const key_value_pair& lhs, const U& rhs){return !operator==(lhs,rhs);} + template friend bool operator> (const key_value_pair& lhs, const U& rhs){return operator< (rhs,lhs);} + template friend bool operator<=(const key_value_pair& lhs, const U& rhs){return !operator> (lhs,rhs);} + template friend bool operator>=(const key_value_pair& lhs, const U& rhs){return !operator< (lhs,rhs);} + }; +} + +namespace std { + template + class hash> { + public: + size_t operator()(const pre::cx::key_value_pair &k) const + { + return std::hash{}(key_.c_str()); + } + }; +} + +namespace pre::cx { + template + auto get_key(const T&) { + return T::key::value.c_str(); + }; + + /*template + inline bool operator==(key_value_pair const& a, key_value_pair const& b) { + return a.value == b.value; + } + template + inline bool operator<(key_value_pair const& a, key_value_pair const& b) { + return a.value < b.value; + }*/ + +} \ No newline at end of file diff --git a/pre/json/detail/dejsonizer.hpp b/pre/json/detail/dejsonizer.hpp index 139ac1df..264f6829 100644 --- a/pre/json/detail/dejsonizer.hpp +++ b/pre/json/detail/dejsonizer.hpp @@ -17,6 +17,12 @@ #include
 
+#include 
+
+#include 
+#include 
+#include 
+
 namespace pre { namespace json { namespace detail {
 
   struct dejsonizer {
@@ -159,6 +165,35 @@ namespace pre { namespace json { namespace detail {
         throw std::runtime_error("Expected " + _json_object.dump() + " to be a json object.");
       }
     }
+
+
+    //! Named fields
+    template
+    void operator()(pre::cx::key_value_pair& value) const {
+        this->operator()(get_key(value), value.value);
+    }
+
+    //! Non adapted structs with key_value_pair
+    template* = nullptr>
+    void operator()(T& value) const {
+
+                
+      auto t = boost::pfr::structure_tie(value);
+      boost::hana::for_each(t, 
+      [this](auto& x) {
+        this->operator()(x);
+      });
+    }
+
+    //! tuples
+    template
+    void operator()(T& value) const {
+      boost::hana::for_each(value, 
+      [this](auto& x) {
+        this->operator()(x);
+      });
+    }
       
     private:
       const nlohmann::json& _json_object; // XXX: Invert to be the same as jsonizer
diff --git a/pre/json/detail/jsonizer.hpp b/pre/json/detail/jsonizer.hpp
index c11ffe1a..0cad4287 100644
--- a/pre/json/detail/jsonizer.hpp
+++ b/pre/json/detail/jsonizer.hpp
@@ -15,6 +15,12 @@
 #include 
 #include 
 
+#include 
+
+#include 
+#include 
+#include 
+
 //TODO: What about tuples ?
 //TODO: What about normal union?
 
@@ -119,6 +125,32 @@ namespace pre { namespace json { namespace detail {
       }
     }
 
+    //! Named fields
+    template
+    void operator()(const pre::cx::key_value_pair& value) const {
+        this->operator()(get_key(value), value.value);
+    }
+
+    //! Non adapted structs with key_value_pair
+    template* = nullptr>
+    void operator()(const T& value) const {
+      auto t = boost::pfr::structure_tie(value);
+      boost::hana::for_each(t, 
+      [this](const auto& x) {
+        this->operator()(x);
+      });
+    }
+
+    //! tuples
+    template
+    void operator()(const T& value) const {
+      boost::hana::for_each(value, 
+      [this](const auto& x) {
+        this->operator()(x);
+      });
+    }
+
     
     private:
       nlohmann::json& _json_object;
diff --git a/pre/json/detail/sfinae_enabler.hpp b/pre/json/detail/sfinae_enabler.hpp
index 583c5d38..8343d512 100644
--- a/pre/json/detail/sfinae_enabler.hpp
+++ b/pre/json/detail/sfinae_enabler.hpp
@@ -50,6 +50,13 @@ namespace pre { namespace json { namespace detail {
       >::value
     ,T>::type;
 
+    template
+    using enable_if_is_struct_non_adapted_t = typename std::enable_if< 
+      ! std::is_same<
+        typename boost::fusion::traits::tag_of::type, 
+        boost::fusion::struct_tag
+      >::value && std::is_aggregate_v
+    ,T>::type;
 
     template
     using enable_if_is_container_t = typename std::enable_if<
@@ -61,7 +68,29 @@ namespace pre { namespace json { namespace detail {
       traits::is_associative_container::value 
     ,T>::type;
 
-
+    namespace requirements {
+      template
+      concept has_tuple_element =
+        requires(T t) {
+          typename std::tuple_element_t>;
+          { get(t) } -> std::convertible_to&>;
+        };
+
+      template
+      concept tuple_like = !std::is_reference_v 
+        && requires(T t) { 
+          typename std::tuple_size::type; 
+          requires std::derived_from<
+            std::tuple_size, 
+            std::integral_constant>
+          >;
+        } && [](std::index_sequence) { 
+          return (has_tuple_element && ...); 
+        }(std::make_index_sequence>());
+
+      template
+      concept not_tuple_like = !tuple_like;
+    }
 }}}
 
 #endif
diff --git a/test/dejsonize_test.cpp b/test/dejsonize_test.cpp
index 084277ca..52e2d1e1 100644
--- a/test/dejsonize_test.cpp
+++ b/test/dejsonize_test.cpp
@@ -7,7 +7,7 @@
 #include 
 #include 
 
-#include 
+#include 
 #include 
 #include 
 
@@ -22,6 +22,7 @@
 namespace datamodel {
 
   using boost::fusion::operator==;
+  using boost::fusion::operator<;
   using boost::fusion::operator!=;
 
   typedef size_t years;
@@ -616,3 +617,115 @@ BOOST_AUTO_TEST_CASE (std_variant_complex) {
 
 
 }
+
+namespace datamodel {
+  struct BuildRunnerRequestDto {
+    pre_json_key(std::string, ssh_public_key);
+    pre_json_key(std::string, environment_name);
+    pre_json_key(std::string, environment_zip_hash);
+    pre_json_key(std::size_t, job_size);
+    pre_json_key(std::string, environment_zip);
+    pre_json_key(cashier, user);
+  };
+  BOOST_PFR_FUNCTIONS_FOR(BuildRunnerRequestDto);
+}
+
+BOOST_AUTO_TEST_CASE (compile_time_named_fields_no_adapt_struct) {
+
+  {
+    using datamodel::BuildRunnerRequestDto;
+    BuildRunnerRequestDto val{ .ssh_public_key = { "that's one key"} };
+
+    auto val_json = pre::json::to_json(val);
+    std::cout << val_json.dump(2) << std::endl;
+
+    auto val_deserialized = pre::json::from_json(val_json); 
+
+    auto val_reserialized = pre::json::to_json(val_deserialized);
+    std::cout << val_reserialized.dump(2) << std::endl;
+
+    BOOST_REQUIRE(val == val_deserialized);
+  }
+
+  {
+    using datamodel::BuildRunnerRequestDto;
+    std::vector vals{
+      {.ssh_public_key = { "that's one key"}, .job_size = {11}},
+      {.ssh_public_key = { "that's second key"}, .job_size = {43}},
+      {.ssh_public_key = { "that's third key"}, .job_size = {13}, .user{{ .section="banananas", .checkout_number=23 }} }
+    };
+
+    auto val_json = pre::json::to_json(vals);
+    std::cout << val_json.dump(2) << std::endl;
+
+    auto val_deserialized = pre::json::from_json(val_json); 
+
+    auto val_reserialized = pre::json::to_json(val_deserialized);
+    std::cout << val_reserialized.dump(2) << std::endl;
+
+    BOOST_REQUIRE(vals == val_deserialized);
+  }
+
+  {
+    using pre::cx::key_value_pair;
+    using namespace datamodel;
+    using namespace std::literals;
+
+    auto val = std::make_tuple(
+      key_value_pair<"ssh_public_key"         , std::string>  {"key"s},
+      key_value_pair<"environment_name"       , std::string>  {"env"s},
+      key_value_pair<"environment_zip_hash"   , std::string>  {"zip_hash"s},
+      key_value_pair<"job_size"               , std::size_t>  {434343},
+      key_value_pair<"environment_zip"        , std::string>  {"zip"s},
+      key_value_pair<"user"                   , cashier>      {{ .section="wine & spirits", .checkout_number=27}} 
+    );
+
+    auto val_json = pre::json::to_json(val);
+    std::cout << val_json.dump(2) << std::endl;
+
+    auto val_deserialized = pre::json::from_json(val_json); 
+
+    auto val_reserialized = pre::json::to_json(val_deserialized);
+    std::cout << val_reserialized.dump(2) << std::endl;
+
+    BOOST_REQUIRE(val == val_deserialized);
+  }
+
+
+    {
+    using pre::cx::key_value_pair;
+    using namespace datamodel;
+    using namespace std::literals;
+
+    auto val = std::make_tuple(
+      key_value_pair<"ssh_public_key"         , std::string>  {"key"s},
+      key_value_pair<"environment_name"       , std::string>  {"env"s},
+      key_value_pair<"environment_zip_hash"   , std::string>  {"zip_hash"s},
+      key_value_pair<"job_size"               , std::size_t>  {434343},
+      key_value_pair<"environment_zip"        , std::string>  {"zip"s},
+      key_value_pair<"user"                   , cashier>      {{ .section="wine & spirits", .checkout_number=27}} 
+    );
+
+    std::vector vals { val };
+    vals.push_back(std::make_tuple(
+      key_value_pair<"ssh_public_key"         , std::string>  {"yoooo"s},
+      key_value_pair<"environment_name"       , std::string>  {"woui"s},
+      key_value_pair<"environment_zip_hash"   , std::string>  {"noooooo"s},
+      key_value_pair<"job_size"               , std::size_t>  {540210},
+      key_value_pair<"environment_zip"        , std::string>  {"timeeeee"s},
+      key_value_pair<"user"                   , cashier>      {{ .section="Gemüse", .checkout_number=3486}} 
+    ));
+
+    auto val_json = pre::json::to_json(vals);
+    std::cout << val_json.dump(2) << std::endl;
+
+    auto val_deserialized = pre::json::from_json(val_json); 
+
+    auto val_reserialized = pre::json::to_json(val_deserialized);
+    std::cout << val_reserialized.dump(2) << std::endl;
+
+    BOOST_REQUIRE(vals == val_deserialized);
+  }
+
+
+}
\ No newline at end of file