250 lines
7.9 KiB
C++
250 lines
7.9 KiB
C++
module;
|
|
|
|
import glaze;
|
|
|
|
export module lsp.codec.transformer;
|
|
|
|
import std;
|
|
|
|
import lsp.codec.common;
|
|
import lsp.protocol.common.basic_types;
|
|
|
|
export namespace lsp::codec
|
|
{
|
|
class LSPAnyConverter
|
|
{
|
|
public:
|
|
template<typename T>
|
|
static protocol::LSPAny ToLSPAny(const T& value);
|
|
|
|
template<typename T>
|
|
static T FromLSPAny(const protocol::LSPAny& any);
|
|
|
|
private:
|
|
template<typename T>
|
|
static T ExtractNumber(const protocol::LSPAny& any);
|
|
|
|
template<typename T>
|
|
static T ConvertViaJson(const protocol::LSPAny& any);
|
|
|
|
template<typename T>
|
|
static protocol::LSPAny SerializeViaJson(const T& obj);
|
|
};
|
|
}
|
|
|
|
// ==================== 实现 ====================
|
|
namespace lsp::codec
|
|
{
|
|
template<typename T>
|
|
protocol::LSPAny LSPAnyConverter::ToLSPAny(const T& value)
|
|
{
|
|
using Type = std::decay_t<T>;
|
|
|
|
if constexpr (std::is_same_v<Type, protocol::LSPAny>)
|
|
{
|
|
return value;
|
|
}
|
|
else if constexpr (std::is_same_v<Type, protocol::LSPObject>)
|
|
{
|
|
return protocol::LSPAny(value);
|
|
}
|
|
else if constexpr (std::is_same_v<Type, protocol::LSPArray>)
|
|
{
|
|
return protocol::LSPAny(value);
|
|
}
|
|
else if constexpr (std::is_same_v<Type, const char*> || std::is_same_v<Type, char*>)
|
|
{
|
|
return protocol::LSPAny(protocol::string(value));
|
|
}
|
|
else if constexpr (is_lsp_basic_type_v<Type>)
|
|
{
|
|
return protocol::LSPAny(value);
|
|
}
|
|
else if constexpr (std::is_integral_v<Type> && !std::is_same_v<Type, bool>)
|
|
{
|
|
return protocol::LSPAny(value);
|
|
}
|
|
else if constexpr (std::is_floating_point_v<Type>)
|
|
{
|
|
return protocol::LSPAny(value);
|
|
}
|
|
else if constexpr (is_vector_v<Type>)
|
|
{
|
|
protocol::LSPArray arr;
|
|
arr.reserve(value.size());
|
|
for (const auto& item : value)
|
|
arr.push_back(ToLSPAny(item));
|
|
return protocol::LSPAny(std::move(arr));
|
|
}
|
|
else if constexpr (is_map_v<Type>)
|
|
{
|
|
protocol::LSPObject obj;
|
|
for (const auto& [key, val] : value)
|
|
obj[key] = ToLSPAny(val);
|
|
return protocol::LSPAny(std::move(obj));
|
|
}
|
|
else if constexpr (is_optional_v<Type>)
|
|
{
|
|
if (value.has_value())
|
|
return ToLSPAny(*value);
|
|
return protocol::LSPAny(nullptr);
|
|
}
|
|
else if constexpr (is_user_struct_v<Type>)
|
|
{
|
|
return SerializeViaJson(value);
|
|
}
|
|
else
|
|
{
|
|
static_assert(!sizeof(Type), "Unsupported type for ToLSPAny");
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
T LSPAnyConverter::FromLSPAny(const protocol::LSPAny& any)
|
|
{
|
|
using Type = std::decay_t<T>;
|
|
|
|
if constexpr (std::is_same_v<Type, protocol::LSPAny>)
|
|
{
|
|
return any;
|
|
}
|
|
else if constexpr (std::is_same_v<Type, protocol::LSPObject>)
|
|
{
|
|
if (!any.Is<protocol::LSPObject>())
|
|
throw ConversionError("LSPAny does not contain LSPObject");
|
|
return any.Get<protocol::LSPObject>();
|
|
}
|
|
else if constexpr (std::is_same_v<Type, protocol::LSPArray>)
|
|
{
|
|
if (!any.Is<protocol::LSPArray>())
|
|
throw ConversionError("LSPAny does not contain LSPArray");
|
|
return any.Get<protocol::LSPArray>();
|
|
}
|
|
else if constexpr (std::is_same_v<Type, bool>)
|
|
{
|
|
if (!any.Is<protocol::boolean>())
|
|
throw ConversionError("LSPAny does not contain a boolean");
|
|
return any.Get<protocol::boolean>();
|
|
}
|
|
else if constexpr (std::is_same_v<Type, protocol::string> || std::is_same_v<Type, std::string>)
|
|
{
|
|
if (!any.Is<protocol::string>())
|
|
throw ConversionError("LSPAny does not contain a string");
|
|
return any.Get<protocol::string>();
|
|
}
|
|
else if constexpr (std::is_arithmetic_v<Type> && !std::is_same_v<Type, bool>)
|
|
{
|
|
return ExtractNumber<Type>(any);
|
|
}
|
|
else if constexpr (is_vector_v<Type>)
|
|
{
|
|
if (!any.Is<protocol::LSPArray>())
|
|
throw ConversionError("LSPAny does not contain an array");
|
|
|
|
const auto& arr = any.Get<protocol::LSPArray>();
|
|
Type result;
|
|
result.reserve(arr.size());
|
|
for (const auto& item : arr)
|
|
result.push_back(FromLSPAny<typename Type::value_type>(item));
|
|
return result;
|
|
}
|
|
else if constexpr (is_map_v<Type>)
|
|
{
|
|
if (!any.Is<protocol::LSPObject>())
|
|
throw ConversionError("LSPAny does not contain an object");
|
|
|
|
const auto& obj = any.Get<protocol::LSPObject>();
|
|
Type result;
|
|
for (const auto& [key, val] : obj)
|
|
result[key] = FromLSPAny<typename Type::mapped_type>(val);
|
|
return result;
|
|
}
|
|
else if constexpr (is_optional_v<Type>)
|
|
{
|
|
if (any.Is<std::nullptr_t>())
|
|
return std::nullopt;
|
|
return FromLSPAny<typename Type::value_type>(any);
|
|
}
|
|
else if constexpr (is_user_struct_v<Type>)
|
|
{
|
|
return ConvertViaJson<Type>(any);
|
|
}
|
|
else
|
|
{
|
|
static_assert(!sizeof(Type), "Unsupported type for FromLSPAny");
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
T LSPAnyConverter::ExtractNumber(const protocol::LSPAny& any)
|
|
{
|
|
if constexpr (std::is_integral_v<T>)
|
|
{
|
|
if (any.Is<protocol::uinteger>())
|
|
return static_cast<T>(any.Get<protocol::uinteger>());
|
|
if (any.Is<protocol::integer>())
|
|
return static_cast<T>(any.Get<protocol::integer>());
|
|
}
|
|
else if constexpr (std::is_floating_point_v<T>)
|
|
{
|
|
if (any.Is<protocol::decimal>())
|
|
return static_cast<T>(any.Get<protocol::decimal>());
|
|
if (any.Is<protocol::integer>())
|
|
return static_cast<T>(any.Get<protocol::integer>());
|
|
if (any.Is<protocol::uinteger>())
|
|
return static_cast<T>(any.Get<protocol::uinteger>());
|
|
}
|
|
|
|
throw ConversionError("LSPAny does not contain a compatible numeric type");
|
|
}
|
|
|
|
template<typename T>
|
|
T LSPAnyConverter::ConvertViaJson(const protocol::LSPAny& any)
|
|
{
|
|
try
|
|
{
|
|
if (any.Is<std::nullptr_t>())
|
|
throw ConversionError("LSPAny is null; cannot convert to target type");
|
|
|
|
std::string json;
|
|
auto ec = glz::write_json(any, json);
|
|
if (ec)
|
|
throw ConversionError("Failed to serialize LSPAny to JSON: " + std::string(glz::format_error(ec, json)));
|
|
|
|
T obj;
|
|
ec = glz::read_json(obj, json);
|
|
if (ec)
|
|
throw ConversionError("Failed to parse JSON to target type: " + std::string(glz::format_error(ec, json)));
|
|
|
|
return obj;
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
throw ConversionError("LSPAny to struct conversion failed: " + std::string(e.what()));
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
protocol::LSPAny LSPAnyConverter::SerializeViaJson(const T& obj)
|
|
{
|
|
try
|
|
{
|
|
std::string json;
|
|
auto ec = glz::write_json(obj, json);
|
|
if (ec)
|
|
throw ConversionError("Failed to serialize struct to JSON: " + std::string(glz::format_error(ec, json)));
|
|
|
|
protocol::LSPAny any;
|
|
ec = glz::read_json(any, json);
|
|
if (ec)
|
|
throw ConversionError("Failed to parse JSON to LSPAny: " + std::string(glz::format_error(ec, json)));
|
|
|
|
return any;
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
throw ConversionError("struct to LSPAny conversion failed: " + std::string(e.what()));
|
|
}
|
|
}
|
|
}
|