From e820d4235b559333b243d9023dcca7807a8e45d7 Mon Sep 17 00:00:00 2001
From: nekosu <liao.hy@outlook.com>
Date: Sun, 16 Feb 2025 16:44:55 +0800
Subject: [PATCH] feat: variant

---
 .gitignore                |  3 ++
 include/common/utils.hpp  | 60 +++++++++++++++++++++++++++++++++++++++
 include/common/value.hpp  | 39 ++++++++++++++++++++-----
 test/serializing_test.cpp |  7 ++++-
 4 files changed, 101 insertions(+), 8 deletions(-)

diff --git a/.gitignore b/.gitignore
index b6e98b0..87b77ae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,3 +44,6 @@ package.json
 package-lock.json
 yarn.lock
 pnpm-lock.yaml
+
+# Clangd
+.cache
diff --git a/include/common/utils.hpp b/include/common/utils.hpp
index e7219b1..8c656e5 100644
--- a/include/common/utils.hpp
+++ b/include/common/utils.hpp
@@ -7,6 +7,8 @@
 #include <sstream>
 #include <string>
 #include <type_traits>
+#include <utility>
+#include <variant>
 
 namespace json
 {
@@ -84,6 +86,11 @@ constexpr bool is_collection = false;
 template <typename T>
 constexpr bool is_collection<T> = is_container<T> && !is_map<T> && !is_fixed_array<T>;
 
+template <typename T>
+constexpr bool is_variant = false;
+template <typename... args_t>
+constexpr bool is_variant<std::variant<args_t...>> = true;
+
 template <typename T>
 class has_to_json_in_member
 {
@@ -261,4 +268,57 @@ inline string_t to_basic_string(any_t&& arg)
         static_assert(!sizeof(any_t), "Unsupported type");
     }
 }
+
+template <std::size_t id, typename string_t, typename variant_t>
+bool serialize_variant_impl(basic_value<string_t>& val, variant_t&& var)
+{
+    if (var.index() == id) {
+        val = basic_value<string_t>(std::get<id>(std::forward<variant_t>(var)));
+        return false;
+    }
+    return true;
+}
+
+template <typename string_t, typename variant_t, std::size_t... ids>
+basic_value<string_t> serialize_variant(variant_t&& var, std::index_sequence<ids...>)
+{
+    basic_value<string_t> val;
+    (serialize_variant_impl<ids>(val, std::forward<variant_t>(var)) && ...);
+    return val;
+}
+
+template <std::size_t id, typename string_t, typename variant_t>
+bool deserialize_variant_impl(const basic_value<string_t>& val, variant_t& var)
+{
+    using alt_t = std::variant_alternative_t<id, variant_t>;
+    if (val.template is<alt_t>()) {
+        var = val.template as<alt_t>();
+        return false;
+    }
+    return true;
+}
+
+template <typename string_t, typename variant_t, std::size_t... ids>
+variant_t deserialize_variant(const basic_value<string_t>& val, std::index_sequence<ids...>)
+{
+    variant_t var;
+    (deserialize_variant_impl<ids>(val, var) && ...);
+    return var;
+}
+
+template <typename string_t, typename alt_t>
+bool detect_variant_impl(const basic_value<string_t>& val)
+{
+    if (val.template is<alt_t>()) {
+        return true;
+    }
+    return false;
+}
+
+template <typename string_t, typename variant_t, std::size_t... ids>
+bool detect_variant(const basic_value<string_t>& val, std::index_sequence<ids...>)
+{
+    return (detect_variant_impl<string_t, std::variant_alternative_t<ids, variant_t>>(val) && ...);
+}
+
 } // namespace json::_utils
diff --git a/include/common/value.hpp b/include/common/value.hpp
index 439c5a7..99892ef 100644
--- a/include/common/value.hpp
+++ b/include/common/value.hpp
@@ -9,6 +9,8 @@
 #include <ostream>
 #include <string>
 #include <tuple>
+#include <type_traits>
+#include <utility>
 #include <variant>
 
 #include "exception.hpp"
@@ -134,6 +136,16 @@ class basic_value
     {
     }
 
+    template <
+        typename variant_t,
+        std::enable_if_t<_utils::is_variant<std::remove_reference_t<variant_t>>, bool> = true>
+    basic_value(variant_t&& var)
+        : basic_value(_utils::serialize_variant<string_t>(
+              std::forward<variant_t>(var),
+              std::make_index_sequence<std::variant_size_v<std::remove_reference_t<variant_t>>>()))
+    {
+    }
+
     template <
         typename value_t,
         std::enable_if_t<!std::is_convertible_v<value_t, basic_value<string_t>>, bool> = true>
@@ -366,13 +378,21 @@ class basic_value
     {
         return as_array().template as_tuple<elem_ts...>();
     }
-    
+
     template <typename elem1_t, typename elem2_t>
     explicit operator std::pair<elem1_t, elem2_t>() const
     {
         return as_array().template as_pair<elem1_t, elem2_t>();
     }
 
+    template <typename... args_t>
+    explicit operator std::variant<args_t...>() const
+    {
+        return _utils::deserialize_variant<string_t, std::variant<args_t...>>(
+            *this,
+            std::make_index_sequence<std::variant_size_v<std::variant<args_t...>>>());
+    }
+
 private:
     friend class basic_array<string_t>;
     friend class basic_object<string_t>;
@@ -566,6 +586,11 @@ inline bool basic_value<string_t>::is() const noexcept
         return is_object() && std::is_constructible_v<string_t, typename value_t::key_type>
                && all<typename value_t::mapped_type>();
     }
+    else if constexpr (_utils::is_variant<value_t>) {
+        return _utils::detect_variant<string_t, value_t>(
+            *this,
+            std::make_index_sequence<std::variant_size_v<value_t>>());
+    }
     else {
         static_assert(!sizeof(value_t), "Unsupported type");
     }
@@ -637,16 +662,16 @@ inline auto basic_value<string_t>::get_helper(
 {
     if constexpr (std::is_constructible_v<string_t, first_key_t>) {
         return is_object() ? as_object().get_helper(
-                   default_value,
-                   std::forward<first_key_t>(first),
-                   std::forward<rest_keys_t>(rest)...)
+                                 default_value,
+                                 std::forward<first_key_t>(first),
+                                 std::forward<rest_keys_t>(rest)...)
                            : default_value;
     }
     else if constexpr (std::is_integral_v<std::decay_t<first_key_t>>) {
         return is_array() ? as_array().get_helper(
-                   default_value,
-                   std::forward<first_key_t>(first),
-                   std::forward<rest_keys_t>(rest)...)
+                                default_value,
+                                std::forward<first_key_t>(first),
+                                std::forward<rest_keys_t>(rest)...)
                           : default_value;
     }
     else {
diff --git a/test/serializing_test.cpp b/test/serializing_test.cpp
index 72492d4..53429c0 100644
--- a/test/serializing_test.cpp
+++ b/test/serializing_test.cpp
@@ -13,6 +13,11 @@
 
 bool serializing()
 {
+    std::variant<int, std::vector<int>> var = 1;
+    json::value k = var;
+    auto var2 = k.as<std::variant<int, std::vector<int>>>();
+    auto is_var = k.is<std::variant<int, std::vector<int>>>();
+
     json::value root;
 
     root["hello"] = "meojson";
@@ -286,7 +291,7 @@ bool jsonizing()
     mine.w = MyStruct::W::C;
 
     json::value j_mine = mine;
-    std::cout << j_mine<< std::endl;
+    std::cout << j_mine << std::endl;
 
     MyStruct new_mine = (MyStruct)j_mine;