Compare commits
3 Commits
33a20fdb52
...
64ec88f0c3
| Author | SHA1 | Date |
|---|---|---|
|
|
64ec88f0c3 | |
|
|
a81d9de71d | |
|
|
c690b76fcf |
|
|
@ -4,3 +4,4 @@ vscode/out
|
|||
lsp-server/build
|
||||
lsp-server/test/test_ast/build
|
||||
lsp-server/test/test_symbol/build
|
||||
lsp-server/test/test_lsp_any/build
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -16,6 +18,7 @@ namespace lsp::protocol
|
|||
using string = std::string;
|
||||
|
||||
// LSPAny 这样设计是为了glaz序列化
|
||||
// export type LSPAny = LSPObject | LSPArray | string | integer | uinteger | decimal | boolean | null;
|
||||
struct LSPAny;
|
||||
using LSPObject = std::map<string, LSPAny>;
|
||||
using LSPArray = std::vector<LSPAny>;
|
||||
|
|
@ -26,7 +29,9 @@ namespace lsp::protocol
|
|||
LSPObject,
|
||||
LSPArray,
|
||||
string,
|
||||
decimal, // 放在前面,优先匹配数字
|
||||
integer,
|
||||
uinteger,
|
||||
decimal,
|
||||
boolean,
|
||||
std::nullptr_t>;
|
||||
|
||||
|
|
@ -34,69 +39,74 @@ namespace lsp::protocol
|
|||
|
||||
// 默认构造函数
|
||||
LSPAny();
|
||||
|
||||
// 拷贝和移动构造函数
|
||||
LSPAny(const LSPAny& other);
|
||||
LSPAny(LSPAny&& other) noexcept;
|
||||
// 针对每种支持的类型的构造函数
|
||||
LSPAny(const std::map<string, LSPAny>& val);
|
||||
LSPAny(std::map<string, LSPAny>&& val);
|
||||
LSPAny(const std::vector<LSPAny>& val);
|
||||
LSPAny(std::vector<LSPAny>&& val);
|
||||
|
||||
// 容器类型的构造函数
|
||||
LSPAny(const LSPObject& val);
|
||||
LSPAny(LSPObject&& val);
|
||||
LSPAny(const LSPArray& val);
|
||||
LSPAny(LSPArray&& val);
|
||||
|
||||
// 字符串类型的构造函数
|
||||
LSPAny(const string& val);
|
||||
LSPAny(string&& val);
|
||||
LSPAny(const char* val);
|
||||
|
||||
// 所有数字类型都转换为 decimal
|
||||
LSPAny(int val);
|
||||
LSPAny(long val);
|
||||
LSPAny(long long val);
|
||||
LSPAny(unsigned int val);
|
||||
LSPAny(unsigned long val);
|
||||
LSPAny(unsigned long long val);
|
||||
// 浮点类型的构造函数 -- decimal
|
||||
LSPAny(float val);
|
||||
LSPAny(double val);
|
||||
LSPAny(long double val);
|
||||
|
||||
// 布尔和空值
|
||||
LSPAny(boolean val);
|
||||
LSPAny(std::nullptr_t);
|
||||
|
||||
// 单个模板处理所有整数类型
|
||||
template<typename T, typename = std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool> && !std::is_same_v<std::decay_t<T>, char>>>
|
||||
LSPAny(T val);
|
||||
|
||||
// 赋值操作符
|
||||
LSPAny& operator=(const LSPAny& other);
|
||||
LSPAny& operator=(LSPAny&& other) noexcept;
|
||||
LSPAny& operator=(const std::map<string, LSPAny>& val);
|
||||
LSPAny& operator=(std::map<string, LSPAny>&& val);
|
||||
LSPAny& operator=(const std::vector<LSPAny>& val);
|
||||
LSPAny& operator=(std::vector<LSPAny>&& val);
|
||||
LSPAny& operator=(const LSPObject& val);
|
||||
LSPAny& operator=(LSPObject&& val);
|
||||
LSPAny& operator=(const LSPArray& val);
|
||||
LSPAny& operator=(LSPArray&& val);
|
||||
|
||||
LSPAny& operator=(const string& val);
|
||||
LSPAny& operator=(string&& val);
|
||||
LSPAny& operator=(const char* val);
|
||||
LSPAny& operator=(int val);
|
||||
LSPAny& operator=(long val);
|
||||
LSPAny& operator=(long long val);
|
||||
LSPAny& operator=(unsigned int val);
|
||||
LSPAny& operator=(unsigned long val);
|
||||
LSPAny& operator=(unsigned long long val);
|
||||
|
||||
LSPAny& operator=(float val);
|
||||
LSPAny& operator=(double val);
|
||||
LSPAny& operator=(long double val);
|
||||
|
||||
LSPAny& operator=(boolean val);
|
||||
LSPAny& operator=(std::nullptr_t);
|
||||
|
||||
// 整数类型赋值 - 使用模板避免类型冲突
|
||||
template<typename T, typename = std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool> && !std::is_same_v<std::decay_t<T>, char>>>
|
||||
LSPAny& operator=(T val);
|
||||
|
||||
// 类型检查辅助函数
|
||||
template<typename T>
|
||||
bool is() const;
|
||||
bool Is() const;
|
||||
|
||||
template<typename T>
|
||||
T& get();
|
||||
T& Get();
|
||||
|
||||
template<typename T>
|
||||
const T& get() const;
|
||||
const T& Get() const;
|
||||
|
||||
// 访问操作符
|
||||
template<typename Visitor>
|
||||
auto visit(Visitor&& visitor) const;
|
||||
auto Visit(Visitor&& visitor) const;
|
||||
|
||||
template<typename Visitor>
|
||||
auto visit(Visitor&& visitor);
|
||||
auto Visit(Visitor&& visitor);
|
||||
};
|
||||
|
||||
using ProgressToken = std::variant<integer, string>;
|
||||
|
|
@ -227,33 +237,75 @@ namespace lsp::protocol
|
|||
};
|
||||
|
||||
// LSP模板方法
|
||||
template<typename T, typename>
|
||||
LSPAny::LSPAny(T val)
|
||||
{
|
||||
if constexpr (std::is_signed_v<T>)
|
||||
{
|
||||
// 有符号整数
|
||||
if (val >= std::numeric_limits<integer>::min() && val <= std::numeric_limits<integer>::max())
|
||||
value = static_cast<integer>(val);
|
||||
else
|
||||
value = static_cast<decimal>(val); // 超出 int32 范围,转为 decimal(可能丢失精度)
|
||||
}
|
||||
else
|
||||
{
|
||||
// 无符号整数
|
||||
if (val <= std::numeric_limits<uinteger>::max())
|
||||
value = static_cast<uinteger>(val);
|
||||
else
|
||||
value = static_cast<decimal>(val); // 超出 uint32 范围,转为 decimal(可能丢失精度)
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename>
|
||||
LSPAny& LSPAny::operator=(T val)
|
||||
{
|
||||
if constexpr (std::is_signed_v<T>)
|
||||
{
|
||||
// 有符号整数
|
||||
if (val >= std::numeric_limits<integer>::min() && val <= std::numeric_limits<integer>::max())
|
||||
value = static_cast<integer>(val);
|
||||
else
|
||||
value = static_cast<decimal>(val);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (val <= std::numeric_limits<uinteger>::max())
|
||||
value = static_cast<uinteger>(val);
|
||||
else
|
||||
value = static_cast<decimal>(val);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool LSPAny::is() const
|
||||
bool LSPAny::Is() const
|
||||
{
|
||||
return std::holds_alternative<T>(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T& LSPAny::get()
|
||||
T& LSPAny::Get()
|
||||
{
|
||||
return std::get<T>(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T& LSPAny::get() const
|
||||
const T& LSPAny::Get() const
|
||||
{
|
||||
return std::get<T>(value);
|
||||
}
|
||||
|
||||
// 访问操作符
|
||||
template<typename Visitor>
|
||||
auto LSPAny::visit(Visitor&& visitor) const
|
||||
auto LSPAny::Visit(Visitor&& visitor) const
|
||||
{
|
||||
return std::visit(std::forward<Visitor>(visitor), value);
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
auto LSPAny::visit(Visitor&& visitor)
|
||||
auto LSPAny::Visit(Visitor&& visitor)
|
||||
{
|
||||
return std::visit(std::forward<Visitor>(visitor), value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#pragma once
|
||||
#include "./basic_types.hpp"
|
||||
// #include "./basic_types.hpp"
|
||||
|
||||
namespace lsp::protocol
|
||||
{
|
||||
|
|
@ -13,16 +13,16 @@ namespace lsp::protocol
|
|||
inline LSPAny::LSPAny(LSPAny&& other) noexcept :
|
||||
value(std::move(other.value)) {}
|
||||
|
||||
inline LSPAny::LSPAny(const std::map<string, LSPAny>& val) :
|
||||
inline LSPAny::LSPAny(const LSPObject& val) :
|
||||
value(val) {}
|
||||
|
||||
inline LSPAny::LSPAny(std::map<string, LSPAny>&& val) :
|
||||
inline LSPAny::LSPAny(LSPObject&& val) :
|
||||
value(std::move(val)) {}
|
||||
|
||||
inline LSPAny::LSPAny(const std::vector<LSPAny>& val) :
|
||||
inline LSPAny::LSPAny(const LSPArray& val) :
|
||||
value(val) {}
|
||||
|
||||
inline LSPAny::LSPAny(std::vector<LSPAny>&& val) :
|
||||
inline LSPAny::LSPAny(LSPArray&& val) :
|
||||
value(std::move(val)) {}
|
||||
|
||||
inline LSPAny::LSPAny(const string& val) :
|
||||
|
|
@ -34,24 +34,6 @@ namespace lsp::protocol
|
|||
inline LSPAny::LSPAny(const char* val) :
|
||||
value(string(val)) {}
|
||||
|
||||
inline LSPAny::LSPAny(int val) :
|
||||
value(static_cast<decimal>(val)) {}
|
||||
|
||||
inline LSPAny::LSPAny(long val) :
|
||||
value(static_cast<decimal>(val)) {}
|
||||
|
||||
inline LSPAny::LSPAny(long long val) :
|
||||
value(static_cast<decimal>(val)) {}
|
||||
|
||||
inline LSPAny::LSPAny(unsigned int val) :
|
||||
value(static_cast<decimal>(val)) {}
|
||||
|
||||
inline LSPAny::LSPAny(unsigned long val) :
|
||||
value(static_cast<decimal>(val)) {}
|
||||
|
||||
inline LSPAny::LSPAny(unsigned long long val) :
|
||||
value(static_cast<decimal>(val)) {}
|
||||
|
||||
inline LSPAny::LSPAny(float val) :
|
||||
value(static_cast<decimal>(val)) {}
|
||||
|
||||
|
|
@ -80,26 +62,25 @@ namespace lsp::protocol
|
|||
return *this;
|
||||
}
|
||||
|
||||
// 针对每种支持的类型的赋值操作符
|
||||
inline LSPAny& LSPAny::operator=(const std::map<string, LSPAny>& val)
|
||||
inline LSPAny& LSPAny::operator=(const LSPObject& val)
|
||||
{
|
||||
value = val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline LSPAny& LSPAny::operator=(std::map<string, LSPAny>&& val)
|
||||
inline LSPAny& LSPAny::operator=(LSPObject&& val)
|
||||
{
|
||||
value = std::move(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline LSPAny& LSPAny::operator=(const std::vector<LSPAny>& val)
|
||||
inline LSPAny& LSPAny::operator=(const LSPArray& val)
|
||||
{
|
||||
value = val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline LSPAny& LSPAny::operator=(std::vector<LSPAny>&& val)
|
||||
inline LSPAny& LSPAny::operator=(LSPArray&& val)
|
||||
{
|
||||
value = std::move(val);
|
||||
return *this;
|
||||
|
|
@ -123,43 +104,6 @@ namespace lsp::protocol
|
|||
return *this;
|
||||
}
|
||||
|
||||
// 所有数字类型都转换为 decimal
|
||||
inline LSPAny& LSPAny::operator=(int val)
|
||||
{
|
||||
value = static_cast<decimal>(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline LSPAny& LSPAny::operator=(long val)
|
||||
{
|
||||
value = static_cast<decimal>(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline LSPAny& LSPAny::operator=(long long val)
|
||||
{
|
||||
value = static_cast<decimal>(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline LSPAny& LSPAny::operator=(unsigned int val)
|
||||
{
|
||||
value = static_cast<decimal>(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline LSPAny& LSPAny::operator=(unsigned long val)
|
||||
{
|
||||
value = static_cast<decimal>(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline LSPAny& LSPAny::operator=(unsigned long long val)
|
||||
{
|
||||
value = static_cast<decimal>(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline LSPAny& LSPAny::operator=(float val)
|
||||
{
|
||||
value = static_cast<decimal>(val);
|
||||
|
|
|
|||
|
|
@ -1,40 +1,105 @@
|
|||
#pragma once
|
||||
#include <type_traits>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
#include "../detail/basic_types.hpp"
|
||||
|
||||
namespace lsp::transform
|
||||
{
|
||||
// 通用错误类
|
||||
/// 转换错误异常类
|
||||
class ConversionError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit ConversionError(const std::string& message)
|
||||
: std::runtime_error("LSP Conversion Error: " + message) {}
|
||||
explicit ConversionError(const std::string& message) : std::runtime_error("LSP Conversion Error: " + message) {}
|
||||
};
|
||||
|
||||
/// 类型特征:LSP 基本类型判断
|
||||
template<typename T>
|
||||
inline constexpr bool is_lsp_basic_type_v =
|
||||
std::is_same_v<T, protocol::integer> ||
|
||||
std::is_same_v<T, protocol::uinteger> ||
|
||||
std::is_same_v<T, protocol::decimal> ||
|
||||
std::is_same_v<T, protocol::boolean> ||
|
||||
std::is_same_v<T, protocol::string> ||
|
||||
std::is_same_v<T, std::nullptr_t>;
|
||||
|
||||
/// 类型特征:LSP 容器类型判断
|
||||
template<typename T>
|
||||
inline constexpr bool is_lsp_container_type_v =
|
||||
std::is_same_v<T, protocol::LSPObject> ||
|
||||
std::is_same_v<T, protocol::LSPArray> ||
|
||||
std::is_same_v<T, protocol::LSPAny>;
|
||||
|
||||
/// 类型特征:检测 vector
|
||||
template<typename T>
|
||||
struct is_vector : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct is_lsp_basic_type: std::false_type {};
|
||||
|
||||
template<> struct is_lsp_basic_type<protocol::boolean> : std::true_type {};
|
||||
template<> struct is_lsp_basic_type<protocol::string> : std::true_type {};
|
||||
template<> struct is_lsp_basic_type<protocol::decimal> : std::true_type {};
|
||||
template<> struct is_lsp_basic_type<std::nullptr_t> : std::true_type {};
|
||||
struct is_vector<std::vector<T>> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct is_lsp_container_type : std::false_type {};
|
||||
inline constexpr bool is_vector_v = is_vector<T>::value;
|
||||
|
||||
template<> struct is_lsp_container_type<protocol::LSPObject> : std::true_type {};
|
||||
template<> struct is_lsp_container_type<protocol::LSPArray> : std::true_type {};
|
||||
template<> struct is_lsp_container_type<protocol::LSPAny> : std::true_type {};
|
||||
|
||||
// struct类型判断
|
||||
/// 类型特征:检测 map
|
||||
template<typename T>
|
||||
struct is_user_struct : std::integral_constant<bool,
|
||||
!std::is_arithmetic<T>::value &&
|
||||
!std::is_same<T, protocol::string>::value &&
|
||||
!std::is_same<T, const char*>::value &&
|
||||
!std::is_same<T, std::nullptr_t>::value &&
|
||||
!is_lsp_basic_type<T>::value &&
|
||||
!is_lsp_container_type<T>::value> {};
|
||||
struct is_map : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template<typename K, typename V>
|
||||
struct is_map<std::map<K, V>> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_map_v = is_map<T>::value;
|
||||
|
||||
/// 类型特征:检测 optional
|
||||
template<typename T>
|
||||
struct is_optional : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct is_optional<std::optional<T>> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_optional_v = is_optional<T>::value;
|
||||
|
||||
/// 类型特征:检测 variant
|
||||
template<typename T>
|
||||
struct is_variant : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
struct is_variant<std::variant<Ts...>> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_variant_v = is_variant<T>::value;
|
||||
|
||||
/// 类型特征:用户自定义 struct 判断
|
||||
template<typename T>
|
||||
inline constexpr bool is_user_struct_v =
|
||||
std::is_class_v<T> &&
|
||||
!std::is_pointer_v<T> &&
|
||||
!std::is_same_v<T, protocol::string> &&
|
||||
!std::is_same_v<T, std::string> &&
|
||||
!is_lsp_basic_type_v<T> &&
|
||||
!is_lsp_container_type_v<T> &&
|
||||
!is_vector_v<T> &&
|
||||
!is_map_v<T> &&
|
||||
!is_optional_v<T> &&
|
||||
!is_variant_v<T>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,106 +3,68 @@
|
|||
|
||||
namespace lsp::transform
|
||||
{
|
||||
// ==================== JSON 序列化/反序列化 ====================
|
||||
|
||||
// ===== 全局便利函数 =====
|
||||
template<typename T>
|
||||
std::optional<T> Deserialize(const std::string& json);
|
||||
|
||||
template<typename T>
|
||||
std::optional<std::string> Serialize(const T& obj);
|
||||
|
||||
// 基本类型
|
||||
template<typename T>
|
||||
protocol::LSPAny LSPAny(const T& obj);
|
||||
// ==================== 转换为 LSPAny ====================
|
||||
|
||||
// // struct 类型
|
||||
// template<typename T>
|
||||
// typename std::enable_if<is_user_struct<T>::value, protocol::LSPAny>::type LSPAny(const T& value);
|
||||
/// 任意类型转换为 LSPAny
|
||||
///
|
||||
/// 支持的类型:
|
||||
/// - 基本类型:bool, int, double, string, nullptr
|
||||
/// - 容器类型:vector, map, optional
|
||||
/// - LSP 类型:LSPObject, LSPArray, LSPAny
|
||||
/// - 用户结构体(需要 glaze 支持)
|
||||
inline constexpr auto ToLSPAny = [](const auto& value) {
|
||||
return LSPAnyConverter::ToLSPAny(value);
|
||||
};
|
||||
|
||||
// 容器类型
|
||||
template<typename T>
|
||||
protocol::LSPAny LSPAny(const std::vector<T>& vec);
|
||||
/// LSPAny 转换为指定类型
|
||||
///
|
||||
/// 支持的类型:
|
||||
/// - 基本类型:bool, int, double, string
|
||||
/// - 容器类型:vector<T>, optional<T>
|
||||
/// - LSP 类型:LSPObject, LSPArray
|
||||
/// - 用户结构体(需要 glaze 支持)
|
||||
inline constexpr auto FromLSPAny = []<typename T>(const protocol::LSPAny& any) -> T {
|
||||
return LSPAnyConverter::FromLSPAny<T>(any);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
protocol::LSPAny LSPAny(const std::map<protocol::string, T>& map);
|
||||
|
||||
template<typename T>
|
||||
protocol::LSPAny LSPAny(const std::optional<T>& opt);
|
||||
|
||||
template<typename T>
|
||||
T As(const protocol::LSPAny& any);
|
||||
|
||||
template<typename T, typename From>
|
||||
std::optional<T> As(const std::optional<From>& opt);
|
||||
|
||||
template<typename T, typename... Ts>
|
||||
T As(const std::variant<Ts...>& var);
|
||||
|
||||
template<typename T>
|
||||
T As(const protocol::LSPObject& obj);
|
||||
|
||||
template<typename T>
|
||||
T As(const protocol::LSPArray& arr);
|
||||
|
||||
template<typename T>
|
||||
T Number(const protocol::LSPAny& any);
|
||||
|
||||
template<typename T>
|
||||
std::vector<T> Vector(const protocol::LSPAny& any);
|
||||
|
||||
template<typename T>
|
||||
std::optional<T> Optional(const protocol::LSPAny& any);
|
||||
|
||||
template<typename T>
|
||||
protocol::LSPObject LSPObject(const T& obj);
|
||||
|
||||
template<typename T>
|
||||
protocol::LSPArray LSPArray(const T& vec);
|
||||
|
||||
protocol::string String(const protocol::LSPAny& any);
|
||||
protocol::boolean Bool(const protocol::LSPAny& any);
|
||||
|
||||
// === 外观接口:类型检查 ===
|
||||
namespace check
|
||||
{
|
||||
inline bool IsObject(const protocol::LSPAny& any) { return any.is<protocol::LSPObject>(); }
|
||||
inline bool IsArray(const protocol::LSPAny& any) { return any.is<protocol::LSPArray>(); }
|
||||
inline bool IsString(const protocol::LSPAny& any) { return any.is<protocol::string>(); }
|
||||
inline bool IsNumber(const protocol::LSPAny& any) { return any.is<protocol::decimal>(); }
|
||||
inline bool IsBool(const protocol::LSPAny& any) { return any.is<protocol::boolean>(); }
|
||||
inline bool IsNull(const protocol::LSPAny& any) { return any.is<std::nullptr_t>(); }
|
||||
/// 检查是否为 LSPObject
|
||||
bool IsObject(const protocol::LSPAny& any);
|
||||
|
||||
/// 检查是否为 LSPArray
|
||||
bool IsArray(const protocol::LSPAny& any);
|
||||
|
||||
/// 检查是否为字符串
|
||||
bool IsString(const protocol::LSPAny& any);
|
||||
|
||||
/// 检查是否为数字(integer/uinteger/decimal)
|
||||
bool IsNumber(const protocol::LSPAny& any);
|
||||
|
||||
/// 检查是否为布尔值
|
||||
bool IsBool(const protocol::LSPAny& any);
|
||||
|
||||
/// 检查是否为 null
|
||||
bool IsNull(const protocol::LSPAny& any);
|
||||
}
|
||||
|
||||
// === 外观接口:调试功能 ===
|
||||
// ==================== 调试工具 ====================
|
||||
|
||||
namespace debug
|
||||
{
|
||||
inline std::string GetTypeName(const protocol::LSPAny& any)
|
||||
{
|
||||
if (any.is<protocol::LSPObject>())
|
||||
return "LSPObject";
|
||||
if (any.is<protocol::LSPArray>())
|
||||
return "LSPArray";
|
||||
if (any.is<protocol::string>())
|
||||
return "string";
|
||||
if (any.is<protocol::decimal>())
|
||||
return "decimal";
|
||||
if (any.is<protocol::boolean>())
|
||||
return "boolean";
|
||||
if (any.is<std::nullptr_t>())
|
||||
return "null";
|
||||
return "unknown";
|
||||
}
|
||||
/// 获取 LSPAny 的类型名称字符串
|
||||
std::string GetTypeName(const protocol::LSPAny& any);
|
||||
|
||||
inline std::string GetIdString(const std::variant<int, std::string>& id)
|
||||
{
|
||||
return std::visit([](const auto& value) -> std::string {
|
||||
if constexpr (std::is_same_v<std::decay_t<decltype(value)>, int>)
|
||||
return std::to_string(value);
|
||||
else
|
||||
return value;
|
||||
}, id);
|
||||
}
|
||||
/// 将 variant<int, string> 转换为字符串(用于 ID)
|
||||
std::string GetIdString(const std::variant<int, std::string>& id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
#pragma once
|
||||
#include "./facade.hpp"
|
||||
// #include "./facade.hpp"
|
||||
|
||||
namespace lsp::transform
|
||||
{
|
||||
// ==================== JSON 序列化/反序列化 ====================
|
||||
|
||||
template<typename T>
|
||||
inline std::optional<T> Deserialize(const std::string& json)
|
||||
{
|
||||
|
|
@ -25,118 +27,77 @@ namespace lsp::transform
|
|||
return json;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline protocol::LSPAny LSPAny(const T& obj)
|
||||
{
|
||||
return LSPAnyConverter::ToLSPAny(obj);
|
||||
}
|
||||
// ==================== 类型检查 ====================
|
||||
|
||||
template<typename T>
|
||||
inline protocol::LSPAny LSPAny(const std::vector<T>& vec)
|
||||
namespace check
|
||||
{
|
||||
return LSPAnyConverter::ToLSPAny(vec);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline protocol::LSPAny LSPAny(const std::map<protocol::string, T>& map)
|
||||
{
|
||||
return LSPAnyConverter::ToLSPAny(map);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline protocol::LSPAny LSPAny(const std::optional<T>& opt)
|
||||
{
|
||||
return LSPAnyConverter::ToLSPAny(opt);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T As(const protocol::LSPAny& any)
|
||||
{
|
||||
return LSPAnyConverter::FromLSPAny<T>(any);
|
||||
}
|
||||
|
||||
template<typename T, typename From>
|
||||
inline std::optional<T> As(const std::optional<From>& opt)
|
||||
{
|
||||
return LSPAnyConverter::As<T>(opt);
|
||||
}
|
||||
|
||||
template<typename T, typename... Ts>
|
||||
inline T As(const std::variant<Ts...>& var)
|
||||
{
|
||||
return LSPAnyConverter::As<T>(var);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T As(const protocol::LSPObject& obj)
|
||||
{
|
||||
return LSPAnyConverter::As<T>(obj);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T As(const protocol::LSPArray& arr)
|
||||
{
|
||||
return LSPAnyConverter::As<T>(arr);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T Number(const protocol::LSPAny& any)
|
||||
{
|
||||
return LSPAnyConverter::ToNumber<T>(any);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline std::vector<T> Vector(const protocol::LSPAny& any)
|
||||
{
|
||||
return LSPAnyConverter::ToVector<T>(any);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline std::optional<T> Optional(const protocol::LSPAny& any)
|
||||
{
|
||||
return LSPAnyConverter::ToOptional<T>(any);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline protocol::LSPObject LSPObject(const T& obj)
|
||||
{
|
||||
// 如果已经是 LSPAny,直接获取其中的 LSPObject
|
||||
if constexpr (std::is_same_v<T, protocol::LSPAny>)
|
||||
inline bool IsObject(const protocol::LSPAny& any)
|
||||
{
|
||||
return LSPAnyConverter::ToLSPObject(obj);
|
||||
return any.Is<protocol::LSPObject>();
|
||||
}
|
||||
else
|
||||
|
||||
inline bool IsArray(const protocol::LSPAny& any)
|
||||
{
|
||||
// 否则先转换为 LSPAny,再获取 LSPObject
|
||||
protocol::LSPAny any = LSPAnyConverter::ToLSPAny(obj);
|
||||
return LSPAnyConverter::ToLSPObject(any);
|
||||
return any.Is<protocol::LSPArray>();
|
||||
}
|
||||
|
||||
inline bool IsString(const protocol::LSPAny& any)
|
||||
{
|
||||
return any.Is<protocol::string>();
|
||||
}
|
||||
|
||||
inline bool IsNumber(const protocol::LSPAny& any)
|
||||
{
|
||||
return any.Is<protocol::integer>() ||
|
||||
any.Is<protocol::uinteger>() ||
|
||||
any.Is<protocol::decimal>();
|
||||
}
|
||||
|
||||
inline bool IsBool(const protocol::LSPAny& any)
|
||||
{
|
||||
return any.Is<protocol::boolean>();
|
||||
}
|
||||
|
||||
inline bool IsNull(const protocol::LSPAny& any)
|
||||
{
|
||||
return any.Is<std::nullptr_t>();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline protocol::LSPArray LSPArray(const T& container)
|
||||
// ==================== 调试工具 ====================
|
||||
|
||||
namespace debug
|
||||
{
|
||||
// 如果已经是 LSPAny,直接获取其中的 LSPArray
|
||||
if constexpr (std::is_same_v<T, protocol::LSPAny>)
|
||||
inline std::string GetTypeName(const protocol::LSPAny& any)
|
||||
{
|
||||
return LSPAnyConverter::ToLSPArray(container);
|
||||
if (any.Is<protocol::LSPObject>())
|
||||
return "LSPObject";
|
||||
if (any.Is<protocol::LSPArray>())
|
||||
return "LSPArray";
|
||||
if (any.Is<protocol::string>())
|
||||
return "string";
|
||||
if (any.Is<protocol::integer>())
|
||||
return "integer";
|
||||
if (any.Is<protocol::uinteger>())
|
||||
return "uinteger";
|
||||
if (any.Is<protocol::decimal>())
|
||||
return "decimal";
|
||||
if (any.Is<protocol::boolean>())
|
||||
return "boolean";
|
||||
if (any.Is<std::nullptr_t>())
|
||||
return "null";
|
||||
return "unknown";
|
||||
}
|
||||
else
|
||||
|
||||
inline std::string GetIdString(const std::variant<int, std::string>& id)
|
||||
{
|
||||
// 否则转换为 LSPAny
|
||||
protocol::LSPAny any = LSPAnyConverter::ToLSPAny(container);
|
||||
return LSPAnyConverter::ToLSPArray(any);
|
||||
return std::visit([](const auto& value) -> std::string {
|
||||
if constexpr (std::is_same_v<std::decay_t<decltype(value)>, int>)
|
||||
return std::to_string(value);
|
||||
else
|
||||
return value;
|
||||
},
|
||||
id);
|
||||
}
|
||||
}
|
||||
|
||||
inline protocol::string String(const protocol::LSPAny& any)
|
||||
{
|
||||
return LSPAnyConverter::ToString(any);
|
||||
}
|
||||
|
||||
inline protocol::boolean Bool(const protocol::LSPAny& any)
|
||||
{
|
||||
return LSPAnyConverter::ToBool(any);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,82 +4,40 @@
|
|||
|
||||
namespace lsp::transform
|
||||
{
|
||||
// LSPAny 序列化/反序列化工具类
|
||||
/// LSPAny 转换核心类
|
||||
/// 提供所有类型的双向转换
|
||||
class LSPAnyConverter
|
||||
{
|
||||
public:
|
||||
// === 基本类型的转换 ===
|
||||
static protocol::LSPAny ToLSPAny(protocol::boolean value);
|
||||
static protocol::LSPAny ToLSPAny(int value);
|
||||
static protocol::LSPAny ToLSPAny(long value);
|
||||
static protocol::LSPAny ToLSPAny(long long value);
|
||||
static protocol::LSPAny ToLSPAny(unsigned int value);
|
||||
static protocol::LSPAny ToLSPAny(unsigned long value);
|
||||
static protocol::LSPAny ToLSPAny(unsigned long long value);
|
||||
static protocol::LSPAny ToLSPAny(float value);
|
||||
static protocol::LSPAny ToLSPAny(double value);
|
||||
static protocol::LSPAny ToLSPAny(long double value);
|
||||
static protocol::LSPAny ToLSPAny(const protocol::string& str);
|
||||
static protocol::LSPAny ToLSPAny(const char* str);
|
||||
static protocol::LSPAny ToLSPAny(std::nullptr_t);
|
||||
static protocol::LSPAny ToLSPAny(const protocol::LSPAny& any);
|
||||
|
||||
// === LSPAny 到基本类型的转换 ===
|
||||
static protocol::boolean ToBool(const protocol::LSPAny& any);
|
||||
static protocol::string ToString(const protocol::LSPAny& any);
|
||||
|
||||
// 其他转换
|
||||
static protocol::LSPObject ToLSPObject(const protocol::LSPAny& any);
|
||||
static protocol::LSPArray ToLSPArray(const protocol::LSPAny& any);
|
||||
|
||||
// === 容器类型的转换 ===
|
||||
// ==================== 任意类型 → LSPAny ====================
|
||||
|
||||
/// 通用转换入口:任意类型 → LSPAny
|
||||
/// 根据类型特征自动分派到具体实现
|
||||
template<typename T>
|
||||
static protocol::LSPAny ToLSPAny(const std::vector<T>& vec);
|
||||
static protocol::LSPAny ToLSPAny(const T& value);
|
||||
|
||||
// ==================== LSPAny → 任意类型 ====================
|
||||
|
||||
/// 通用转换入口:LSPAny → 任意类型
|
||||
/// 根据类型特征自动分派到具体实现
|
||||
template<typename T>
|
||||
static protocol::LSPAny ToLSPAny(const std::map<protocol::string, T>& map);
|
||||
|
||||
template<typename T>
|
||||
static protocol::LSPAny ToLSPAny(const std::optional<T>& opt);
|
||||
|
||||
// === Struct 到 LSPAny 的转换 ===
|
||||
template<typename T>
|
||||
static typename std::enable_if<is_user_struct<T>::value, protocol::LSPAny>::type ToLSPAny(const T& obj);
|
||||
|
||||
template<typename T>
|
||||
static typename std::enable_if<std::is_arithmetic<T>::value && !std::is_same<T, protocol::boolean>::value, T>::type ToNumber(const protocol::LSPAny& any);
|
||||
|
||||
// === LSPAny 到容器类型的转换 ===
|
||||
template<typename T>
|
||||
static std::vector<T> ToVector(const protocol::LSPAny& any);
|
||||
|
||||
template<typename T>
|
||||
static std::optional<T> ToOptional(const protocol::LSPAny& any);
|
||||
|
||||
template<typename T>
|
||||
static typename std::enable_if<is_user_struct<T>::value, T>::type FromLSPAny(const protocol::LSPAny& any);
|
||||
|
||||
// 处理 std::optional<T>
|
||||
template<typename T, typename From>
|
||||
static std::optional<T> As(const std::optional<From>& opt);
|
||||
|
||||
// 处理 std::variant<Ts...>
|
||||
template<typename T, typename... Ts>
|
||||
static T As(const std::variant<Ts...>& var);
|
||||
|
||||
// 处理 LSPObject 和 LSPArray
|
||||
template<typename T>
|
||||
static T As(const protocol::LSPObject& obj);
|
||||
|
||||
template<typename T>
|
||||
static T As(const protocol::LSPArray& arr);
|
||||
|
||||
static T FromLSPAny(const protocol::LSPAny& any);
|
||||
|
||||
private:
|
||||
// ==================== 辅助工具 ====================
|
||||
|
||||
/// 提取数字(支持三种数字类型)
|
||||
template<typename T>
|
||||
static T FromLSPAnyImpl(const protocol::LSPAny& any);
|
||||
};
|
||||
static T ExtractNumber(const protocol::LSPAny& any);
|
||||
|
||||
/// 通过 JSON 转换用户结构体
|
||||
template<typename T>
|
||||
static T ConvertViaJson(const protocol::LSPAny& any);
|
||||
|
||||
/// 将用户结构体序列化为 LSPAny
|
||||
template<typename T>
|
||||
static protocol::LSPAny SerializeViaJson(const T& obj);
|
||||
};
|
||||
}
|
||||
|
||||
#include "./transformer.inl"
|
||||
|
|
|
|||
|
|
@ -1,249 +1,210 @@
|
|||
#pragma once
|
||||
#include "./transformer.hpp"
|
||||
|
||||
namespace lsp::transform
|
||||
{
|
||||
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(protocol::boolean value)
|
||||
{
|
||||
return protocol::LSPAny(value);
|
||||
}
|
||||
|
||||
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(int value)
|
||||
{
|
||||
return protocol::LSPAny(value);
|
||||
}
|
||||
|
||||
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(long value)
|
||||
{
|
||||
return protocol::LSPAny(value);
|
||||
}
|
||||
|
||||
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(long long value)
|
||||
{
|
||||
return protocol::LSPAny(value);
|
||||
}
|
||||
|
||||
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(unsigned int value)
|
||||
{
|
||||
return protocol::LSPAny(value);
|
||||
}
|
||||
|
||||
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(unsigned long value)
|
||||
{
|
||||
return protocol::LSPAny(value);
|
||||
}
|
||||
|
||||
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(unsigned long long value)
|
||||
{
|
||||
return protocol::LSPAny(value);
|
||||
}
|
||||
|
||||
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(float value)
|
||||
{
|
||||
return protocol::LSPAny(value);
|
||||
}
|
||||
|
||||
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(double value)
|
||||
{
|
||||
return protocol::LSPAny(value);
|
||||
}
|
||||
|
||||
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(long double value)
|
||||
{
|
||||
return protocol::LSPAny(value);
|
||||
}
|
||||
|
||||
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(const protocol::string& str)
|
||||
{
|
||||
return protocol::LSPAny(str);
|
||||
}
|
||||
|
||||
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(const char* str)
|
||||
{
|
||||
return protocol::LSPAny(str);
|
||||
}
|
||||
|
||||
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(std::nullptr_t)
|
||||
{
|
||||
return protocol::LSPAny(nullptr);
|
||||
}
|
||||
|
||||
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(const protocol::LSPAny& any)
|
||||
{
|
||||
return any;
|
||||
}
|
||||
|
||||
inline protocol::boolean LSPAnyConverter::ToBool(const protocol::LSPAny& any)
|
||||
{
|
||||
if (!any.is<protocol::boolean>())
|
||||
throw ConversionError("LSPAny does not contain a boolean");
|
||||
return any.get<protocol::boolean>();
|
||||
}
|
||||
|
||||
inline protocol::string LSPAnyConverter::ToString(const protocol::LSPAny& any)
|
||||
{
|
||||
if (!any.is<protocol::string>())
|
||||
throw ConversionError("LSPAny does not contain a string");
|
||||
return any.get<protocol::string>();
|
||||
}
|
||||
|
||||
inline protocol::LSPObject LSPAnyConverter::ToLSPObject(const protocol::LSPAny& any)
|
||||
{
|
||||
if (!any.is<protocol::LSPObject>())
|
||||
throw ConversionError("LSPAny does not contain LSPObject");
|
||||
return any.get<protocol::LSPObject>();
|
||||
}
|
||||
|
||||
inline protocol::LSPArray LSPAnyConverter::ToLSPArray(const protocol::LSPAny& any)
|
||||
{
|
||||
if (!any.is<protocol::LSPArray>())
|
||||
throw ConversionError("LSPAny does not contain LSPArray");
|
||||
return any.get<protocol::LSPArray>();
|
||||
}
|
||||
|
||||
// 模板实现
|
||||
template<typename T>
|
||||
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(const std::vector<T>& vec)
|
||||
{
|
||||
protocol::LSPArray arr;
|
||||
arr.reserve(vec.size());
|
||||
for (const auto& item : vec)
|
||||
arr.push_back(ToLSPAny(item));
|
||||
return protocol::LSPAny(std::move(arr));
|
||||
}
|
||||
// ==================== ToLSPAny 实现 ====================
|
||||
|
||||
template<typename T>
|
||||
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(const std::map<protocol::string, T>& map)
|
||||
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(const T& value)
|
||||
{
|
||||
protocol::LSPObject obj;
|
||||
for (const auto& [key, value] : map)
|
||||
obj[key] = ToLSPAny(value);
|
||||
return protocol::LSPAny(std::move(obj));
|
||||
}
|
||||
using Type = std::decay_t<T>;
|
||||
|
||||
template<typename T>
|
||||
inline protocol::LSPAny LSPAnyConverter::ToLSPAny(const std::optional<T>& opt)
|
||||
{
|
||||
if (opt.has_value())
|
||||
return ToLSPAny(*opt);
|
||||
return protocol::LSPAny(nullptr);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline typename std::enable_if<is_user_struct<T>::value, protocol::LSPAny>::type
|
||||
LSPAnyConverter::ToLSPAny(const T& obj)
|
||||
{
|
||||
try
|
||||
// LSPAny 直接返回
|
||||
if constexpr (std::is_same_v<Type, protocol::LSPAny>)
|
||||
{
|
||||
// 使用glaze序列化为JSON字符串
|
||||
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)));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
// LSPObject 直接返回
|
||||
else if constexpr (std::is_same_v<Type, protocol::LSPObject>)
|
||||
{
|
||||
return protocol::LSPAny(value);
|
||||
}
|
||||
// LSPArray 直接返回
|
||||
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));
|
||||
}
|
||||
// LSP 基本类型(boolean, string, decimal, nullptr)
|
||||
else if constexpr (is_lsp_basic_type_v<Type>)
|
||||
{
|
||||
return protocol::LSPAny(value);
|
||||
}
|
||||
// 整数类型(依赖 LSPAny 模板构造函数)
|
||||
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);
|
||||
}
|
||||
// vector → LSPArray
|
||||
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));
|
||||
}
|
||||
// map → LSPObject
|
||||
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));
|
||||
}
|
||||
// optional → LSPAny or null
|
||||
else if constexpr (is_optional_v<Type>)
|
||||
{
|
||||
if (value.has_value())
|
||||
return ToLSPAny(*value);
|
||||
return protocol::LSPAny(nullptr);
|
||||
}
|
||||
// 用户结构体 → LSPAny (通过 JSON)
|
||||
else if constexpr (is_user_struct_v<Type>)
|
||||
{
|
||||
return SerializeViaJson(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(!sizeof(Type), "Unsupported type for ToLSPAny");
|
||||
}
|
||||
}
|
||||
|
||||
// 直接解析为LSPAny
|
||||
protocol::LSPAny result;
|
||||
ec = glz::read_json(result, json);
|
||||
if (ec)
|
||||
{
|
||||
throw ConversionError("Failed to parse JSON to LSPAny: " + std::string(glz::format_error(ec, json)));
|
||||
}
|
||||
// ==================== FromLSPAny 实现 ====================
|
||||
|
||||
template<typename T>
|
||||
inline T LSPAnyConverter::FromLSPAny(const protocol::LSPAny& any)
|
||||
{
|
||||
using Type = std::decay_t<T>;
|
||||
|
||||
// LSPAny 直接返回
|
||||
if constexpr (std::is_same_v<Type, protocol::LSPAny>)
|
||||
{
|
||||
return any;
|
||||
}
|
||||
// LSPObject 提取
|
||||
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>();
|
||||
}
|
||||
// LSPArray 提取
|
||||
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>();
|
||||
}
|
||||
// boolean 提取
|
||||
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>();
|
||||
}
|
||||
// string 提取
|
||||
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);
|
||||
}
|
||||
// vector 提取
|
||||
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;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
// map 提取
|
||||
else if constexpr (is_map_v<Type>)
|
||||
{
|
||||
throw ConversionError("struct to LSPAny conversion failed: " + std::string(e.what()));
|
||||
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;
|
||||
}
|
||||
// optional 提取
|
||||
else if constexpr (is_optional_v<Type>)
|
||||
{
|
||||
if (any.Is<std::nullptr_t>())
|
||||
return std::nullopt;
|
||||
return FromLSPAny<typename Type::value_type>(any);
|
||||
}
|
||||
// 用户结构体提取(通过 JSON)
|
||||
else if constexpr (is_user_struct_v<Type>)
|
||||
{
|
||||
return ConvertViaJson<Type>(any);
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(!sizeof(Type), "Unsupported type for FromLSPAny");
|
||||
}
|
||||
}
|
||||
|
||||
// Fromprotocol::LSPAny 实现
|
||||
// ==================== 辅助工具实现 ====================
|
||||
|
||||
template<typename T>
|
||||
inline typename std::enable_if<std::is_arithmetic<T>::value && !std::is_same<T, protocol::boolean>::value, T>::type LSPAnyConverter::ToNumber(const protocol::LSPAny& any)
|
||||
inline T LSPAnyConverter::ExtractNumber(const protocol::LSPAny& any)
|
||||
{
|
||||
if (!any.is<protocol::decimal>())
|
||||
throw ConversionError("LSPAny does not contain a number");
|
||||
return static_cast<T>(any.get<protocol::decimal>());
|
||||
if (any.Is<protocol::integer>())
|
||||
{
|
||||
return static_cast<T>(any.Get<protocol::integer>());
|
||||
}
|
||||
else if (any.Is<protocol::uinteger>())
|
||||
{
|
||||
return static_cast<T>(any.Get<protocol::uinteger>());
|
||||
}
|
||||
else if (any.Is<protocol::decimal>())
|
||||
{
|
||||
return static_cast<T>(any.Get<protocol::decimal>());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw ConversionError("LSPAny does not contain a number (integer/uinteger/decimal)");
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline std::vector<T> LSPAnyConverter::ToVector(const protocol::LSPAny& any)
|
||||
{
|
||||
if (!any.is<protocol::LSPArray>())
|
||||
throw ConversionError("LSPAny does not contain an array");
|
||||
|
||||
const auto& arr = any.get<protocol::LSPArray>();
|
||||
std::vector<T> result;
|
||||
result.reserve(arr.size());
|
||||
for (const auto& item : arr)
|
||||
result.push_back(FromLSPAny<T>(item));
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline std::optional<T> LSPAnyConverter::ToOptional(const protocol::LSPAny& any)
|
||||
{
|
||||
if (any.is<std::nullptr_t>())
|
||||
return std::nullopt;
|
||||
return FromLSPAny<T>(any);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline typename std::enable_if<is_user_struct<T>::value, T>::type
|
||||
LSPAnyConverter::FromLSPAny(const protocol::LSPAny& any)
|
||||
{
|
||||
return FromLSPAnyImpl<T>(any);
|
||||
}
|
||||
|
||||
// 处理 std::optional<T>
|
||||
template<typename T, typename From>
|
||||
inline std::optional<T> LSPAnyConverter::As(const std::optional<From>& opt)
|
||||
{
|
||||
if (opt.has_value())
|
||||
return As<T>(*opt);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// 处理 std::variant<Ts...>
|
||||
template<typename T, typename... Ts>
|
||||
inline T LSPAnyConverter::As(const std::variant<Ts...>& var)
|
||||
{
|
||||
return std::visit([](const auto& val) -> T {
|
||||
return As<T>(val);
|
||||
}, var);
|
||||
}
|
||||
|
||||
// 处理 LSPObject
|
||||
template<typename T>
|
||||
inline T LSPAnyConverter::As(const protocol::LSPObject& obj)
|
||||
{
|
||||
protocol::LSPAny any(obj);
|
||||
return FromLSPAny<T>(any);
|
||||
}
|
||||
|
||||
// 处理 LSPArray
|
||||
template<typename T>
|
||||
inline T LSPAnyConverter::As(const protocol::LSPArray& arr)
|
||||
{
|
||||
protocol::LSPAny any(arr);
|
||||
return FromLSPAny<T>(any);
|
||||
}
|
||||
|
||||
// FromLSPAnyImpl 实现
|
||||
template<typename T>
|
||||
inline T LSPAnyConverter::FromLSPAnyImpl(const protocol::LSPAny& any)
|
||||
inline T LSPAnyConverter::ConvertViaJson(const protocol::LSPAny& any)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 序列化LSPAny为JSON
|
||||
// 序列化 LSPAny 为 JSON
|
||||
std::string json;
|
||||
auto ec = glz::write_json(any.value, json);
|
||||
if (ec)
|
||||
throw ConversionError("Failed to serialize LSPAny to JSON: " + std::string(glz::format_error(ec, json)));
|
||||
|
||||
// 解析JSON到目标类型
|
||||
// 解析 JSON 到目标类型
|
||||
T result;
|
||||
ec = glz::read_json(result, json);
|
||||
if (ec)
|
||||
|
|
@ -256,4 +217,28 @@ namespace lsp::transform
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline protocol::LSPAny LSPAnyConverter::SerializeViaJson(const T& obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用 glaze 序列化为 JSON 字符串
|
||||
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)));
|
||||
|
||||
// 直接解析为 LSPAny
|
||||
protocol::LSPAny result;
|
||||
ec = glz::read_json(result, json);
|
||||
if (ec)
|
||||
throw ConversionError("Failed to parse JSON to LSPAny: " + std::string(glz::format_error(ec, json)));
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw ConversionError("struct to LSPAny conversion failed: " + std::string(e.what()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,308 +0,0 @@
|
|||
#include <iostream>
|
||||
#include <vector>
|
||||
#include "../../src/protocol/protocol.hpp"
|
||||
#include "../../src/protocol/transform/facade.hpp"
|
||||
|
||||
using namespace lsp::protocol;
|
||||
using namespace lsp;
|
||||
|
||||
template<typename T>
|
||||
void print_json(const string& name, const T& obj)
|
||||
{
|
||||
if (auto result = glz::write_json(obj); result)
|
||||
{
|
||||
std::cout << name << " = " << *result << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Error: " << result.error() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void test_basic_conversion()
|
||||
{
|
||||
std::cout << "\n=== Testing Basic Conversion ===" << std::endl;
|
||||
|
||||
// 测试 struct 转换
|
||||
RequestMessage msg;
|
||||
msg.method = "textDocument/completion";
|
||||
msg.id = 42;
|
||||
|
||||
// Struct -> LSPAny
|
||||
LSPAny any = transform::LSPAny(msg);
|
||||
std::string json;
|
||||
auto ce = glz::write_json(any, json);
|
||||
if (ce)
|
||||
std::cout << "Error" << std::endl;
|
||||
std::cout << "Message as JSON: " << json << std::endl;
|
||||
|
||||
// LSPAny -> Struct
|
||||
RequestMessage restored = transform::As<RequestMessage>(any);
|
||||
std::cout << "Restored method: " << restored.method << std::endl;
|
||||
std::cout << "id = ";
|
||||
std::visit([](const auto& value) {
|
||||
std::cout << value;
|
||||
},
|
||||
restored.id);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void test_vector_conversion()
|
||||
{
|
||||
std::cout << "\n=== Testing Vector Conversion ===" << std::endl;
|
||||
|
||||
std::vector<Position> positions = {
|
||||
{ 10, 5 },
|
||||
{ 20, 15 },
|
||||
{ 30, 25 }
|
||||
};
|
||||
|
||||
// Vector -> LSPAny
|
||||
LSPAny any = transform::LSPAny(positions);
|
||||
|
||||
std::string json;
|
||||
auto ce = glz::write_json(any, json);
|
||||
std::cout << "Vector as JSON: " << json << std::endl;
|
||||
|
||||
// LSPAny -> Vector
|
||||
auto restored = transform::As<std::vector<Position>>(any);
|
||||
std::cout << "Restored " << restored.size() << " positions" << std::endl;
|
||||
}
|
||||
|
||||
void test_json_string()
|
||||
{
|
||||
std::cout << "\n=== Test json_string ===" << std::endl;
|
||||
|
||||
string json = R"({"jsonrpc":"2.0","id":1,"method":"initialize","params":{"processId":null,"rootUri":"file:///tmp","workDoneToken":"abc"}})";
|
||||
RequestMessage request;
|
||||
auto ret = glz::read_json(request, json);
|
||||
std::cout << "jsonrpc = " << request.jsonrpc << std::endl;
|
||||
std::cout << "method = " << request.method << std::endl;
|
||||
std::cout << "id = ";
|
||||
std::visit([](const auto& value) {
|
||||
std::cout << value;
|
||||
},
|
||||
request.id);
|
||||
std::cout << std::endl;
|
||||
|
||||
auto result = glz::write_json(request.params);
|
||||
std::cout << *result << std::endl;
|
||||
InitializeParams params;
|
||||
// std::string buffer = R"({"workDoneToken":123,"processId":"id","clientInfo":{"name":"VSCode","version":"1.0.0"},"rootPath":"456","initializationOptions":"options","trace":"messages"})";
|
||||
auto r2 = glz::read_json(params, *result);
|
||||
if (params.processId.has_value())
|
||||
std::cout << "processId = " << params.processId.value() << "\n";
|
||||
else
|
||||
std::cout << "processId = null\n";
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void test_basic_types()
|
||||
{
|
||||
std::cout << "\n=== Test basic_types.hpp ===" << std::endl;
|
||||
Range range = {
|
||||
.start = { 1, 3 },
|
||||
.end = { 2, 4 }
|
||||
};
|
||||
|
||||
DocumentFilter df = {
|
||||
.language = "en",
|
||||
.scheme = "scheme",
|
||||
.pattern = "pattern",
|
||||
};
|
||||
|
||||
AnnotatedTextEdit ate;
|
||||
ate.range = { .start = { 1, 3 }, .end = { 2, 4 } };
|
||||
ate.newText = "text_edit";
|
||||
ate.annotationId = "id";
|
||||
|
||||
Location location = {
|
||||
.uri = "location_uri",
|
||||
.range = { .start = { 1, 3 }, .end = { 2, 4 } }
|
||||
};
|
||||
|
||||
Command command1 = {
|
||||
.title = "command_title",
|
||||
.command = "command_command",
|
||||
};
|
||||
Command command2 = {
|
||||
.title = "command_title",
|
||||
.command = "command_command",
|
||||
.arguments = "any_string"
|
||||
};
|
||||
Command command3 = {
|
||||
.title = "command_title",
|
||||
.command = "command_command",
|
||||
.arguments = 123
|
||||
};
|
||||
std::vector<LSPAny> array = {
|
||||
1.0, // decimal
|
||||
string("two"),
|
||||
3.0, // decimal
|
||||
true,
|
||||
nullptr
|
||||
};
|
||||
LSPAny array_val = array;
|
||||
LSPObject object = {
|
||||
{ "name", string("test object") },
|
||||
{ "count", 42.0 }, // decimal
|
||||
{ "active", boolean(true) },
|
||||
{ "data", array_val },
|
||||
{ "metadata", nullptr }
|
||||
};
|
||||
Command command4 = {
|
||||
.title = "command_title",
|
||||
.command = "command_command",
|
||||
.arguments = object
|
||||
};
|
||||
|
||||
MarkdownClientCapabilities mkcp = {
|
||||
.parser = "parse",
|
||||
.allowedTags = std::vector<string>{ "h1", "h2", "p", "code", "pre" }
|
||||
};
|
||||
std::cout << "Test....basic_types" << std::endl;
|
||||
print_json("Range", range);
|
||||
print_json("DocumentFilter", df);
|
||||
print_json("AnnotatedTextEdit", ate);
|
||||
print_json("Location", location);
|
||||
print_json("Command1", command1);
|
||||
print_json("Command2", command2);
|
||||
print_json("Command3", command3);
|
||||
print_json("Command4", command4);
|
||||
print_json("MarkdownClientCapabilities", mkcp);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void test_capabilities()
|
||||
{
|
||||
std::cout << "\n=== Test capabilities.hpp ===" << std::endl;
|
||||
InitializeParams ip;
|
||||
ip.workDoneToken = 123;
|
||||
ip.processId = std::nullopt;
|
||||
ip.clientInfo = InitializeParams::ClientInfo{ "VSCode", "1.0.0" };
|
||||
// ip.rootPath = "456";
|
||||
ip.initializationOptions = "options";
|
||||
ip.trace = TraceValueLiterals::Messages;
|
||||
|
||||
std::cout << "Test....capabilities" << std::endl;
|
||||
print_json("InitializeParams", ip);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void test_RequestMessage_serialize()
|
||||
{
|
||||
// // 1. 直接从 LSPObject 转换
|
||||
// lsp::protocol::LSPObject obj = [> ... <];
|
||||
// auto result = lsp::transform::As<CompletionParams>(obj);
|
||||
|
||||
// // 2. 从 variant 转换
|
||||
// std::variant<lsp::protocol::LSPArray, lsp::protocol::LSPObject> var = [> ... <];
|
||||
// auto result = lsp::transform::As<CompletionParams>(var);
|
||||
|
||||
// // 3. 从 optional 转换
|
||||
// std::optional<lsp::protocol::LSPObject> opt = [> ... <];
|
||||
// auto result = lsp::transform::As<CompletionParams>(opt); // 返回 optional<CompletionParams>
|
||||
|
||||
// // 4. 从复杂的嵌套类型转换
|
||||
// std::optional<std::variant<lsp::protocol::LSPArray, lsp::protocol::LSPObject>> complex = [> ... <];
|
||||
// auto result = lsp::transform::As<CompletionParams>(complex); // 返回 optional<CompletionParams>
|
||||
|
||||
// // 5. 实际使用场景
|
||||
// if (request.params.has_value()) {
|
||||
// // 直接处理 optional<variant<LSPArray, LSPObject>>
|
||||
// auto completionParams = lsp::transform::As<lsp::protocol::CompletionParams>(request.params);
|
||||
// if (completionParams.has_value()) {
|
||||
// std::cout << "URI: " << completionParams->textDocument.uri << std::endl;
|
||||
// }
|
||||
// }
|
||||
std::string json = R"({
|
||||
"jsonrpc":"2.0",
|
||||
"id":"4",
|
||||
"method":"textDocument/completion",
|
||||
"params":{
|
||||
"context":{"triggerKind":1},
|
||||
"partialResultToken":0,
|
||||
"position":{"character":12,"line":22},
|
||||
"textDocument":{"uri":"file://path/to_file.ts"},
|
||||
"workDoneToken":0
|
||||
}
|
||||
})";
|
||||
|
||||
RequestMessage request;
|
||||
auto error = glz::read_json(request, json);
|
||||
|
||||
if (error)
|
||||
{
|
||||
std::cerr << "Failed to parse JSON: " << glz::format_error(error, json) << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "jsonrpc: " << request.jsonrpc << std::endl;
|
||||
std::cout << "method: " << request.method << std::endl;
|
||||
std::visit([](const auto& id) {
|
||||
using T = std::decay_t<decltype(id)>;
|
||||
if constexpr (std::is_same_v<T, lsp::protocol::string>)
|
||||
{
|
||||
std::cout << "ID (string): " << id << std::endl;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, lsp::protocol::integer>)
|
||||
{
|
||||
std::cout << "ID (integer): " << id << std::endl;
|
||||
}
|
||||
},
|
||||
request.id);
|
||||
|
||||
auto completionParams = transform::As<CompletionParams>(request.params.value());
|
||||
std::cout << "URI: " << completionParams.textDocument.uri << std::endl;
|
||||
|
||||
// std::visit([](const auto& value)
|
||||
// {
|
||||
// std::cout << value;
|
||||
// }, request.id);
|
||||
|
||||
// std::visit([](const auto& params) {
|
||||
|
||||
// }, request.params.value());
|
||||
}
|
||||
|
||||
void test_message()
|
||||
{
|
||||
std::cout << "\n=== Test message.hpp ===" << std::endl;
|
||||
ResponseMessage response;
|
||||
response.id = "123";
|
||||
InitializeResult result;
|
||||
result.serverInfo.name = "TSL Language Server";
|
||||
result.serverInfo.version = "1.0.0";
|
||||
TextDocumentSyncOptions opts;
|
||||
opts.openClose = true;
|
||||
opts.change = TextDocumentSyncKind::Incremental;
|
||||
result.capabilities.textDocumentSync = opts;
|
||||
|
||||
CompletionParams comparams;
|
||||
comparams.textDocument.uri = "file://path/to_file.ts";
|
||||
comparams.position.character = 12;
|
||||
comparams.position.line = 22;
|
||||
comparams.context = CompletionContext{
|
||||
.triggerKind = CompletionTriggerKind::Invoked,
|
||||
.triggerCharacter = std::nullopt
|
||||
};
|
||||
RequestMessage rm;
|
||||
rm.id = "4";
|
||||
rm.method = "textDocument/completion";
|
||||
rm.params = transform::LSPObject(comparams);
|
||||
|
||||
print_json("ResponseMessage", response);
|
||||
print_json("rm", rm);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// test_basic_conversion();
|
||||
// test_vector_conversion();
|
||||
// test_json_string();
|
||||
// test_basic_types();
|
||||
// test_capabilities();
|
||||
test_message();
|
||||
test_RequestMessage_serialize();
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,848 +0,0 @@
|
|||
#include <glaze/glaze.hpp>
|
||||
#include <variant>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
|
||||
using integer = std::int32_t;
|
||||
using uinteger = std::uint32_t;
|
||||
using decimal = double;
|
||||
using boolean = bool;
|
||||
using string = std::string;
|
||||
|
||||
// 前向声明
|
||||
struct LSPAny;
|
||||
using LSPObject = std::map<string, LSPAny>;
|
||||
using LSPArray = std::vector<LSPAny>;
|
||||
|
||||
using LSPAnyVariant = std::variant<
|
||||
LSPObject,
|
||||
LSPArray,
|
||||
string,
|
||||
decimal, // 放在前面,优先匹配数字
|
||||
boolean,
|
||||
std::nullptr_t>;
|
||||
|
||||
struct LSPAny
|
||||
{
|
||||
LSPAnyVariant value;
|
||||
|
||||
// 默认构造函数
|
||||
LSPAny() :
|
||||
value(nullptr) {}
|
||||
|
||||
// 拷贝和移动构造函数
|
||||
LSPAny(const LSPAny& other) :
|
||||
value(other.value) {}
|
||||
LSPAny(LSPAny&& other) noexcept :
|
||||
value(std::move(other.value)) {}
|
||||
|
||||
// 针对每种支持的类型的构造函数
|
||||
LSPAny(const std::map<string, LSPAny>& val) :
|
||||
value(val) {}
|
||||
LSPAny(std::map<string, LSPAny>&& val) :
|
||||
value(std::move(val)) {}
|
||||
|
||||
LSPAny(const std::vector<LSPAny>& val) :
|
||||
value(val) {}
|
||||
LSPAny(std::vector<LSPAny>&& val) :
|
||||
value(std::move(val)) {}
|
||||
|
||||
LSPAny(const string& val) :
|
||||
value(val) {}
|
||||
LSPAny(string&& val) :
|
||||
value(std::move(val)) {}
|
||||
LSPAny(const char* val) :
|
||||
value(string(val)) {}
|
||||
|
||||
// 所有数字类型都转换为 decimal
|
||||
LSPAny(int val) :
|
||||
value(static_cast<decimal>(val)) {}
|
||||
LSPAny(long val) :
|
||||
value(static_cast<decimal>(val)) {}
|
||||
LSPAny(long long val) :
|
||||
value(static_cast<decimal>(val)) {}
|
||||
LSPAny(unsigned int val) :
|
||||
value(static_cast<decimal>(val)) {}
|
||||
LSPAny(unsigned long val) :
|
||||
value(static_cast<decimal>(val)) {}
|
||||
LSPAny(unsigned long long val) :
|
||||
value(static_cast<decimal>(val)) {}
|
||||
LSPAny(float val) :
|
||||
value(static_cast<decimal>(val)) {}
|
||||
LSPAny(double val) :
|
||||
value(val) {}
|
||||
LSPAny(long double val) :
|
||||
value(static_cast<decimal>(val)) {}
|
||||
|
||||
LSPAny(boolean val) :
|
||||
value(val) {}
|
||||
LSPAny(std::nullptr_t) :
|
||||
value(nullptr) {}
|
||||
|
||||
// 赋值操作符
|
||||
LSPAny& operator=(const LSPAny& other)
|
||||
{
|
||||
value = other.value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
LSPAny& operator=(LSPAny&& other) noexcept
|
||||
{
|
||||
value = std::move(other.value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// 针对每种支持的类型的赋值操作符
|
||||
LSPAny& operator=(const std::map<string, LSPAny>& val)
|
||||
{
|
||||
value = val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
LSPAny& operator=(std::map<string, LSPAny>&& val)
|
||||
{
|
||||
value = std::move(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LSPAny& operator=(const std::vector<LSPAny>& val)
|
||||
{
|
||||
value = val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
LSPAny& operator=(std::vector<LSPAny>&& val)
|
||||
{
|
||||
value = std::move(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LSPAny& operator=(const string& val)
|
||||
{
|
||||
value = val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
LSPAny& operator=(string&& val)
|
||||
{
|
||||
value = std::move(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LSPAny& operator=(const char* val)
|
||||
{
|
||||
value = string(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// 所有数字类型都转换为 decimal
|
||||
LSPAny& operator=(int val)
|
||||
{
|
||||
value = static_cast<decimal>(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LSPAny& operator=(long val)
|
||||
{
|
||||
value = static_cast<decimal>(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LSPAny& operator=(long long val)
|
||||
{
|
||||
value = static_cast<decimal>(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LSPAny& operator=(unsigned int val)
|
||||
{
|
||||
value = static_cast<decimal>(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LSPAny& operator=(unsigned long val)
|
||||
{
|
||||
value = static_cast<decimal>(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LSPAny& operator=(unsigned long long val)
|
||||
{
|
||||
value = static_cast<decimal>(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LSPAny& operator=(float val)
|
||||
{
|
||||
value = static_cast<decimal>(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LSPAny& operator=(double val)
|
||||
{
|
||||
value = val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
LSPAny& operator=(long double val)
|
||||
{
|
||||
value = static_cast<decimal>(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LSPAny& operator=(boolean val)
|
||||
{
|
||||
value = val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
LSPAny& operator=(std::nullptr_t)
|
||||
{
|
||||
value = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// 类型检查辅助函数
|
||||
template<typename T>
|
||||
bool is() const
|
||||
{
|
||||
return std::holds_alternative<T>(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T& get()
|
||||
{
|
||||
return std::get<T>(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T& get() const
|
||||
{
|
||||
return std::get<T>(value);
|
||||
}
|
||||
|
||||
// 访问操作符
|
||||
template<typename Visitor>
|
||||
auto visit(Visitor&& visitor) const
|
||||
{
|
||||
return std::visit(std::forward<Visitor>(visitor), value);
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
auto visit(Visitor&& visitor)
|
||||
{
|
||||
return std::visit(std::forward<Visitor>(visitor), value);
|
||||
}
|
||||
};
|
||||
|
||||
// glaze 自动支持 std::variant,无需额外配置
|
||||
namespace glz
|
||||
{
|
||||
template<>
|
||||
struct meta<LSPAny>
|
||||
{
|
||||
using T = LSPAny;
|
||||
static constexpr auto value = &T::value;
|
||||
};
|
||||
}
|
||||
|
||||
// ===== 简单清晰的转换模板 =====
|
||||
|
||||
// 转换工具类
|
||||
struct LSPConvert
|
||||
{
|
||||
// === 基本类型的转换(优先级最高) ===
|
||||
|
||||
// boolean
|
||||
static LSPAny ToLSPAny(boolean value)
|
||||
{
|
||||
return LSPAny(value);
|
||||
}
|
||||
|
||||
// 整数类型
|
||||
static LSPAny ToLSPAny(int value)
|
||||
{
|
||||
return LSPAny(value);
|
||||
}
|
||||
|
||||
static LSPAny ToLSPAny(long value)
|
||||
{
|
||||
return LSPAny(value);
|
||||
}
|
||||
|
||||
static LSPAny ToLSPAny(long long value)
|
||||
{
|
||||
return LSPAny(value);
|
||||
}
|
||||
|
||||
static LSPAny ToLSPAny(unsigned int value)
|
||||
{
|
||||
return LSPAny(value);
|
||||
}
|
||||
|
||||
static LSPAny ToLSPAny(unsigned long value)
|
||||
{
|
||||
return LSPAny(value);
|
||||
}
|
||||
|
||||
static LSPAny ToLSPAny(unsigned long long value)
|
||||
{
|
||||
return LSPAny(value);
|
||||
}
|
||||
|
||||
// 浮点类型
|
||||
static LSPAny ToLSPAny(float value)
|
||||
{
|
||||
return LSPAny(value);
|
||||
}
|
||||
|
||||
static LSPAny ToLSPAny(double value)
|
||||
{
|
||||
return LSPAny(value);
|
||||
}
|
||||
|
||||
static LSPAny ToLSPAny(long double value)
|
||||
{
|
||||
return LSPAny(value);
|
||||
}
|
||||
|
||||
// string
|
||||
static LSPAny ToLSPAny(const string& str)
|
||||
{
|
||||
return LSPAny(str);
|
||||
}
|
||||
|
||||
static LSPAny ToLSPAny(const char* str)
|
||||
{
|
||||
return LSPAny(str);
|
||||
}
|
||||
|
||||
// nullptr
|
||||
static LSPAny ToLSPAny(std::nullptr_t)
|
||||
{
|
||||
return LSPAny(nullptr);
|
||||
}
|
||||
|
||||
// LSPAny 自身
|
||||
static LSPAny ToLSPAny(const LSPAny& any)
|
||||
{
|
||||
return any;
|
||||
}
|
||||
|
||||
// === 容器类型的转换 ===
|
||||
|
||||
// vector
|
||||
template<typename T>
|
||||
static LSPAny ToLSPAny(const std::vector<T>& vec)
|
||||
{
|
||||
LSPArray arr;
|
||||
arr.reserve(vec.size());
|
||||
for (const auto& item : vec)
|
||||
{
|
||||
arr.push_back(ToLSPAny(item));
|
||||
}
|
||||
return LSPAny(std::move(arr));
|
||||
}
|
||||
|
||||
// map
|
||||
template<typename T>
|
||||
static LSPAny ToLSPAny(const std::map<string, T>& map)
|
||||
{
|
||||
LSPObject obj;
|
||||
for (const auto& [key, value] : map)
|
||||
{
|
||||
obj[key] = ToLSPAny(value);
|
||||
}
|
||||
return LSPAny(std::move(obj));
|
||||
}
|
||||
|
||||
// optional
|
||||
template<typename T>
|
||||
static LSPAny ToLSPAny(const std::optional<T>& opt)
|
||||
{
|
||||
if (opt.has_value())
|
||||
{
|
||||
return ToLSPAny(*opt);
|
||||
}
|
||||
return LSPAny(nullptr);
|
||||
}
|
||||
|
||||
// === Struct 到 LSPAny 的转换(最低优先级) ===
|
||||
template<typename T>
|
||||
static typename std::enable_if<
|
||||
!std::is_arithmetic<T>::value &&
|
||||
!std::is_same<T, string>::value &&
|
||||
!std::is_same<T, const char*>::value &&
|
||||
!std::is_same<T, std::nullptr_t>::value &&
|
||||
!std::is_same<T, LSPAny>::value,
|
||||
LSPAny>::type
|
||||
ToLSPAny(const T& obj)
|
||||
{
|
||||
// 序列化为 JSON 字符串
|
||||
std::string json;
|
||||
auto ec = glz::write_json(obj, json);
|
||||
if (ec)
|
||||
{
|
||||
throw std::runtime_error("Failed to serialize to JSON");
|
||||
}
|
||||
|
||||
// 直接解析为 LSPAny
|
||||
LSPAny result;
|
||||
ec = glz::read_json(result, json);
|
||||
if (ec)
|
||||
{
|
||||
throw std::runtime_error("Failed to parse JSON to LSPAny");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// === LSPAny 到基本类型的转换 ===
|
||||
|
||||
// boolean
|
||||
static boolean BoolFromLSPAny(const LSPAny& any)
|
||||
{
|
||||
if (!any.is<boolean>())
|
||||
{
|
||||
throw std::runtime_error("LSPAny is not a boolean");
|
||||
}
|
||||
return any.get<boolean>();
|
||||
}
|
||||
|
||||
// 数字类型
|
||||
template<typename T>
|
||||
static typename std::enable_if<std::is_arithmetic<T>::value && !std::is_same<T, boolean>::value, T>::type
|
||||
NumberFromLSPAny(const LSPAny& any)
|
||||
{
|
||||
if (!any.is<decimal>())
|
||||
{
|
||||
throw std::runtime_error("LSPAny is not a number");
|
||||
}
|
||||
return static_cast<T>(any.get<decimal>());
|
||||
}
|
||||
|
||||
// string
|
||||
static string StringFromLSPAny(const LSPAny& any)
|
||||
{
|
||||
if (!any.is<string>())
|
||||
{
|
||||
throw std::runtime_error("LSPAny is not a string");
|
||||
}
|
||||
return any.get<string>();
|
||||
}
|
||||
|
||||
// === LSPAny 到容器类型的转换 ===
|
||||
|
||||
// vector
|
||||
template<typename T>
|
||||
static std::vector<T> VectorFromLSPAny(const LSPAny& any)
|
||||
{
|
||||
if (!any.is<LSPArray>())
|
||||
{
|
||||
throw std::runtime_error("LSPAny is not an array");
|
||||
}
|
||||
|
||||
const auto& arr = any.get<LSPArray>();
|
||||
std::vector<T> result;
|
||||
result.reserve(arr.size());
|
||||
for (const auto& item : arr)
|
||||
{
|
||||
result.push_back(FromLSPAny<T>(item));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// optional
|
||||
template<typename T>
|
||||
static std::optional<T> OptionalFromLSPAny(const LSPAny& any)
|
||||
{
|
||||
if (any.is<std::nullptr_t>())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
return FromLSPAny<T>(any);
|
||||
}
|
||||
|
||||
// === LSPAny 到 Struct 的转换 ===
|
||||
template<typename T>
|
||||
static T FromLSPAny(const LSPAny& any)
|
||||
{
|
||||
return FromLSPAnyImpl<T>(any);
|
||||
}
|
||||
|
||||
private:
|
||||
// 内部实现,使用重载来处理不同类型
|
||||
|
||||
// boolean
|
||||
static boolean FromLSPAnyImpl(const LSPAny& any, boolean*)
|
||||
{
|
||||
return BoolFromLSPAny(any);
|
||||
}
|
||||
|
||||
// string
|
||||
static string FromLSPAnyImpl(const LSPAny& any, string*)
|
||||
{
|
||||
return StringFromLSPAny(any);
|
||||
}
|
||||
|
||||
// 数字类型
|
||||
template<typename T>
|
||||
static typename std::enable_if<std::is_arithmetic<T>::value && !std::is_same<T, boolean>::value, T>::type
|
||||
FromLSPAnyImpl(const LSPAny& any, T*)
|
||||
{
|
||||
return NumberFromLSPAny<T>(any);
|
||||
}
|
||||
|
||||
// LSPAny 自身
|
||||
static LSPAny FromLSPAnyImpl(const LSPAny& any, LSPAny*)
|
||||
{
|
||||
return any;
|
||||
}
|
||||
|
||||
// struct 类型
|
||||
template<typename T>
|
||||
static typename std::enable_if<
|
||||
!std::is_arithmetic<T>::value &&
|
||||
!std::is_same<T, string>::value &&
|
||||
!std::is_same<T, LSPAny>::value,
|
||||
T>::type
|
||||
FromLSPAnyImpl(const LSPAny& any, T*)
|
||||
{
|
||||
// 序列化 LSPAny 为 JSON
|
||||
std::string json;
|
||||
auto ec = glz::write_json(any.value, json);
|
||||
if (ec)
|
||||
{
|
||||
throw std::runtime_error("Failed to serialize LSPAny to JSON");
|
||||
}
|
||||
|
||||
// 解析 JSON 到目标类型
|
||||
T result;
|
||||
ec = glz::read_json(result, json);
|
||||
if (ec)
|
||||
{
|
||||
throw std::runtime_error("Failed to parse JSON to target type");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 通过指针类型来分派
|
||||
template<typename T>
|
||||
static T FromLSPAnyImpl(const LSPAny& any)
|
||||
{
|
||||
return FromLSPAnyImpl(any, static_cast<T*>(nullptr));
|
||||
}
|
||||
};
|
||||
|
||||
// ===== 便利函数(可选) =====
|
||||
|
||||
// 基本类型的便利函数
|
||||
inline LSPAny ToLSPAny(boolean value) { return LSPConvert::ToLSPAny(value); }
|
||||
inline LSPAny ToLSPAny(int value) { return LSPConvert::ToLSPAny(value); }
|
||||
inline LSPAny ToLSPAny(long value) { return LSPConvert::ToLSPAny(value); }
|
||||
inline LSPAny ToLSPAny(long long value) { return LSPConvert::ToLSPAny(value); }
|
||||
inline LSPAny ToLSPAny(unsigned int value) { return LSPConvert::ToLSPAny(value); }
|
||||
inline LSPAny ToLSPAny(unsigned long value) { return LSPConvert::ToLSPAny(value); }
|
||||
inline LSPAny ToLSPAny(unsigned long long value) { return LSPConvert::ToLSPAny(value); }
|
||||
inline LSPAny ToLSPAny(float value) { return LSPConvert::ToLSPAny(value); }
|
||||
inline LSPAny ToLSPAny(double value) { return LSPConvert::ToLSPAny(value); }
|
||||
inline LSPAny ToLSPAny(long double value) { return LSPConvert::ToLSPAny(value); }
|
||||
inline LSPAny ToLSPAny(const string& str) { return LSPConvert::ToLSPAny(str); }
|
||||
inline LSPAny ToLSPAny(const char* str) { return LSPConvert::ToLSPAny(str); }
|
||||
inline LSPAny ToLSPAny(std::nullptr_t) { return LSPConvert::ToLSPAny(nullptr); }
|
||||
inline LSPAny ToLSPAny(const LSPAny& any) { return LSPConvert::ToLSPAny(any); }
|
||||
|
||||
// 容器类型的便利函数
|
||||
template<typename T>
|
||||
LSPAny ToLSPAny(const std::vector<T>& vec)
|
||||
{
|
||||
return LSPConvert::ToLSPAny(vec);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
LSPAny ToLSPAny(const std::map<string, T>& map)
|
||||
{
|
||||
return LSPConvert::ToLSPAny(map);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
LSPAny ToLSPAny(const std::optional<T>& opt)
|
||||
{
|
||||
return LSPConvert::ToLSPAny(opt);
|
||||
}
|
||||
|
||||
// struct 类型的便利函数
|
||||
template<typename T>
|
||||
typename std::enable_if<
|
||||
!std::is_arithmetic<T>::value &&
|
||||
!std::is_same<T, string>::value &&
|
||||
!std::is_same<T, const char*>::value &&
|
||||
!std::is_same<T, std::nullptr_t>::value &&
|
||||
!std::is_same<T, LSPAny>::value,
|
||||
LSPAny>::type
|
||||
ToLSPAny(const T& value)
|
||||
{
|
||||
return LSPConvert::ToLSPAny(value);
|
||||
}
|
||||
|
||||
// FromLSPAny 便利函数
|
||||
template<typename T>
|
||||
T FromLSPAny(const LSPAny& any)
|
||||
{
|
||||
return LSPConvert::FromLSPAny<T>(any);
|
||||
}
|
||||
|
||||
// ===== 测试代码 =====
|
||||
|
||||
// 测试用的结构体
|
||||
struct Message
|
||||
{
|
||||
string jsonrpc = "2.0";
|
||||
string method;
|
||||
int id = 0;
|
||||
};
|
||||
|
||||
struct Position
|
||||
{
|
||||
int line;
|
||||
int character;
|
||||
};
|
||||
|
||||
struct Range
|
||||
{
|
||||
Position start;
|
||||
Position end;
|
||||
};
|
||||
|
||||
struct TextDocumentIdentifier
|
||||
{
|
||||
string uri;
|
||||
};
|
||||
|
||||
struct TextDocumentPositionParams
|
||||
{
|
||||
TextDocumentIdentifier textDocument;
|
||||
Position position;
|
||||
};
|
||||
|
||||
struct CompletionItem
|
||||
{
|
||||
string label;
|
||||
std::optional<int> kind;
|
||||
std::optional<string> detail;
|
||||
std::optional<string> documentation;
|
||||
std::optional<boolean> deprecated;
|
||||
std::optional<boolean> preselect;
|
||||
std::optional<string> sortText;
|
||||
std::optional<string> filterText;
|
||||
std::optional<string> insertText;
|
||||
};
|
||||
|
||||
// 为测试结构体提供 glaze 元数据
|
||||
template<>
|
||||
struct glz::meta<Message>
|
||||
{
|
||||
using T = Message;
|
||||
static constexpr auto value = object(
|
||||
"jsonrpc",
|
||||
&T::jsonrpc,
|
||||
"method",
|
||||
&T::method,
|
||||
"id",
|
||||
&T::id);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct glz::meta<Position>
|
||||
{
|
||||
using T = Position;
|
||||
static constexpr auto value = object(
|
||||
"line",
|
||||
&T::line,
|
||||
"character",
|
||||
&T::character);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct glz::meta<Range>
|
||||
{
|
||||
using T = Range;
|
||||
static constexpr auto value = object(
|
||||
"start",
|
||||
&T::start,
|
||||
"end",
|
||||
&T::end);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct glz::meta<TextDocumentIdentifier>
|
||||
{
|
||||
using T = TextDocumentIdentifier;
|
||||
static constexpr auto value = object(
|
||||
"uri",
|
||||
&T::uri);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct glz::meta<TextDocumentPositionParams>
|
||||
{
|
||||
using T = TextDocumentPositionParams;
|
||||
static constexpr auto value = object(
|
||||
"textDocument",
|
||||
&T::textDocument,
|
||||
"position",
|
||||
&T::position);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct glz::meta<CompletionItem>
|
||||
{
|
||||
using T = CompletionItem;
|
||||
static constexpr auto value = object(
|
||||
"label",
|
||||
&T::label,
|
||||
"kind",
|
||||
&T::kind,
|
||||
"detail",
|
||||
&T::detail,
|
||||
"documentation",
|
||||
&T::documentation,
|
||||
"deprecated",
|
||||
&T::deprecated,
|
||||
"preselect",
|
||||
&T::preselect,
|
||||
"sortText",
|
||||
&T::sortText,
|
||||
"filterText",
|
||||
&T::filterText,
|
||||
"insertText",
|
||||
&T::insertText);
|
||||
};
|
||||
|
||||
// 测试函数
|
||||
#include <iostream>
|
||||
|
||||
void test_basic_conversion()
|
||||
{
|
||||
std::cout << "=== Testing Basic Conversion ===" << std::endl;
|
||||
|
||||
// 测试 struct 转换
|
||||
Message msg;
|
||||
msg.method = "textDocument/completion";
|
||||
msg.id = 42;
|
||||
|
||||
// Struct -> LSPAny
|
||||
LSPAny any = ToLSPAny(msg);
|
||||
|
||||
// 序列化查看
|
||||
std::string json;
|
||||
glz::write_json(any, json);
|
||||
std::cout << "Message as JSON: " << json << std::endl;
|
||||
|
||||
// LSPAny -> Struct
|
||||
Message restored = FromLSPAny<Message>(any);
|
||||
std::cout << "Restored method: " << restored.method << std::endl;
|
||||
std::cout << "Restored id: " << restored.id << std::endl;
|
||||
}
|
||||
|
||||
void test_vector_conversion()
|
||||
{
|
||||
std::cout << "\n=== Testing Vector Conversion ===" << std::endl;
|
||||
|
||||
std::vector<Position> positions = {
|
||||
{ 10, 5 },
|
||||
{ 20, 15 },
|
||||
{ 30, 25 }
|
||||
};
|
||||
|
||||
// Vector -> LSPAny
|
||||
LSPAny any = ToLSPAny(positions);
|
||||
|
||||
std::string json;
|
||||
glz::write_json(any, json);
|
||||
std::cout << "Vector as JSON: " << json << std::endl;
|
||||
|
||||
// LSPAny -> Vector
|
||||
auto restored = LSPConvert::VectorFromLSPAny<Position>(any);
|
||||
std::cout << "Restored " << restored.size() << " positions" << std::endl;
|
||||
}
|
||||
|
||||
void test_optional_conversion()
|
||||
{
|
||||
std::cout << "\n=== Testing Optional Conversion ===" << std::endl;
|
||||
|
||||
CompletionItem item;
|
||||
item.label = "std::vector";
|
||||
item.kind = 1;
|
||||
item.detail = "template<typename T> class vector";
|
||||
|
||||
LSPAny any = ToLSPAny(item);
|
||||
|
||||
std::string json;
|
||||
glz::write_json(any, json);
|
||||
std::cout << "CompletionItem as JSON: " << json << std::endl;
|
||||
|
||||
// 测试 null optional
|
||||
std::optional<string> empty_opt;
|
||||
LSPAny null_any = ToLSPAny(empty_opt);
|
||||
glz::write_json(null_any, json);
|
||||
std::cout << "Empty optional as JSON: " << json << std::endl;
|
||||
}
|
||||
|
||||
void test_mixed_usage()
|
||||
{
|
||||
std::cout << "\n=== Testing Mixed Usage ===" << std::endl;
|
||||
|
||||
// 创建复杂对象
|
||||
LSPObject obj;
|
||||
obj["message"] = ToLSPAny(Message{ "2.0", "test/method", 123 });
|
||||
obj["position"] = ToLSPAny(Position{ 10, 20 });
|
||||
obj["numbers"] = ToLSPAny(std::vector<int>{ 1, 2, 3 });
|
||||
obj["flag"] = LSPAny(true);
|
||||
obj["value"] = LSPAny(3.14);
|
||||
obj["text"] = LSPAny("Hello LSP");
|
||||
obj["empty"] = LSPAny(nullptr);
|
||||
|
||||
LSPAny root(obj);
|
||||
|
||||
std::string json;
|
||||
glz::write_json(root, json);
|
||||
std::cout << "Complex object: " << json << std::endl;
|
||||
|
||||
// 提取值
|
||||
if (root.is<LSPObject>())
|
||||
{
|
||||
auto& root_obj = root.get<LSPObject>();
|
||||
|
||||
Message msg = FromLSPAny<Message>(root_obj["message"]);
|
||||
std::cout << "Extracted message method: " << msg.method << std::endl;
|
||||
|
||||
Position pos = FromLSPAny<Position>(root_obj["position"]);
|
||||
std::cout << "Extracted position: " << pos.line << ", " << pos.character << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
try
|
||||
{
|
||||
test_basic_conversion();
|
||||
test_vector_conversion();
|
||||
test_optional_conversion();
|
||||
test_mixed_usage();
|
||||
|
||||
std::cout << "\nAll tests completed!" << std::endl;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cout << "Exception: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
cmake_minimum_required(VERSION 4.0)
|
||||
|
||||
project(test_lsp_any)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
message(STATUS "CMAKE_CXX_COMPILER_ID: ${CMAKE_CXX_COMPILER_ID}")
|
||||
message(STATUS "CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}")
|
||||
message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
|
||||
|
||||
if (DEFINED CMAKE_TOOLCHAIN_FILE)
|
||||
message(STATUS ">>> CMAKE_TOOLCHAIN_FILE: ${CMAKE_TOOLCHAIN_FILE}")
|
||||
endif()
|
||||
if (DEFINED VCPKG_TARGET_TRIPLET)
|
||||
message(STATUS ">>> VCPKG_TARGET_TRIPLET: ${VCPKG_TARGET_TRIPLET}")
|
||||
endif()
|
||||
|
||||
# 设置默认构建类型
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
set(CMAKE_BUILD_TYPE
|
||||
"Release"
|
||||
CACHE STRING "Build type" FORCE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
|
||||
"MinSizeRel" "RelWithDebInfo")
|
||||
endif()
|
||||
|
||||
# MinGW/MSYS2 静态链接
|
||||
if(MINGW)
|
||||
add_link_options(-static -static-libgcc -static-libstdc++)
|
||||
elseif(UNIX AND NOT APPLE) # Linux 静态链接
|
||||
add_link_options(-static-libgcc -static-libstdc++)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ".lib" ".dll.a")
|
||||
else()
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ".so")
|
||||
endif()
|
||||
|
||||
find_package(glaze CONFIG REQUIRED)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
find_package(Threads REQUIRED)
|
||||
endif()
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
set(SOURCES
|
||||
./common_test.cpp
|
||||
./facade_test.cpp
|
||||
./lsp_any_test.cpp
|
||||
./test_framework.cpp
|
||||
./test_main.cpp
|
||||
./transformer_test.cpp
|
||||
)
|
||||
|
||||
add_executable(${PROJECT_NAME} ${SOURCES})
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE src)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
glaze::glaze
|
||||
$<$<PLATFORM_ID:Linux>:Threads::Threads> # 使用生成器表达式
|
||||
)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE
|
||||
-Wall -Wextra -Wpedantic
|
||||
$<$<CONFIG:Debug>:-g -O0>
|
||||
$<$<CONFIG:Release>:-O3>
|
||||
)
|
||||
endif()
|
||||
|
||||
|
|
@ -0,0 +1,325 @@
|
|||
#include "common_test.hpp"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
#include "../../src/protocol/transform/common.hpp"
|
||||
#include "../../src/protocol/detail/basic_types.hpp"
|
||||
|
||||
namespace lsp::test
|
||||
{
|
||||
// 用于测试的用户自定义结构体
|
||||
struct UserStruct
|
||||
{
|
||||
int value;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
void CommonTests::registerTests(TestRunner& runner)
|
||||
{
|
||||
// is_lsp_basic_type 测试
|
||||
runner.addTest("Common - is_lsp_basic_type (integer)", testIsLSPBasicInteger);
|
||||
runner.addTest("Common - is_lsp_basic_type (uinteger)", testIsLSPBasicUInteger);
|
||||
runner.addTest("Common - is_lsp_basic_type (boolean)", testIsLSPBasicBoolean);
|
||||
runner.addTest("Common - is_lsp_basic_type (string)", testIsLSPBasicString);
|
||||
runner.addTest("Common - is_lsp_basic_type (decimal)", testIsLSPBasicDecimal);
|
||||
runner.addTest("Common - is_lsp_basic_type (nullptr_t)", testIsLSPBasicNullptr);
|
||||
runner.addTest("Common - is_lsp_basic_type (负面案例)", testIsLSPBasicNegativeCases);
|
||||
|
||||
// is_lsp_container_type 测试
|
||||
runner.addTest("Common - is_lsp_container_type (LSPObject)", testIsLSPContainerObject);
|
||||
runner.addTest("Common - is_lsp_container_type (LSPArray)", testIsLSPContainerArray);
|
||||
runner.addTest("Common - is_lsp_container_type (LSPAny)", testIsLSPContainerAny);
|
||||
runner.addTest("Common - is_lsp_container_type (负面案例)", testIsLSPContainerNegativeCases);
|
||||
|
||||
// is_user_struct 测试
|
||||
runner.addTest("Common - is_user_struct (用户结构体)", testIsUserStructPositive);
|
||||
runner.addTest("Common - is_user_struct (排除算术类型)", testIsUserStructExcludeArithmetic);
|
||||
runner.addTest("Common - is_user_struct (排除字符串)", testIsUserStructExcludeString);
|
||||
runner.addTest("Common - is_user_struct (排除LSP类型)", testIsUserStructExcludeLSPTypes);
|
||||
runner.addTest("Common - is_user_struct (排除vector)", testIsUserStructExcludeVector);
|
||||
runner.addTest("Common - is_user_struct (排除map)", testIsUserStructExcludeMap);
|
||||
runner.addTest("Common - is_user_struct (排除optional)", testIsUserStructExcludeOptional);
|
||||
runner.addTest("Common - is_user_struct (排除variant)", testIsUserStructExcludeVariant);
|
||||
runner.addTest("Common - is_user_struct (排除指针)", testIsUserStructExcludePointers);
|
||||
}
|
||||
|
||||
// ==================== is_lsp_basic_type 测试 ====================
|
||||
|
||||
TestResult CommonTests::testIsLSPBasicInteger()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
bool is_basic = transform::is_lsp_basic_type_v<protocol::integer>;
|
||||
assertTrue(is_basic, "integer应该是LSP基本类型");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult CommonTests::testIsLSPBasicUInteger()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
bool is_basic = transform::is_lsp_basic_type_v<protocol::uinteger>;
|
||||
assertTrue(is_basic, "uinteger应该是LSP基本类型");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult CommonTests::testIsLSPBasicBoolean()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
bool is_basic = transform::is_lsp_basic_type_v<protocol::boolean>;
|
||||
assertTrue(is_basic, "boolean应该是LSP基本类型");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult CommonTests::testIsLSPBasicString()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
bool is_basic = transform::is_lsp_basic_type_v<protocol::string>;
|
||||
assertTrue(is_basic, "string应该是LSP基本类型");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult CommonTests::testIsLSPBasicDecimal()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
bool is_basic = transform::is_lsp_basic_type_v<protocol::decimal>;
|
||||
assertTrue(is_basic, "decimal应该是LSP基本类型");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult CommonTests::testIsLSPBasicNullptr()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
bool is_basic = transform::is_lsp_basic_type_v<std::nullptr_t>;
|
||||
assertTrue(is_basic, "nullptr_t应该是LSP基本类型");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult CommonTests::testIsLSPBasicNegativeCases()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
// 测试明确不是 LSP 基本类型的类型
|
||||
// 注意:避免测试 int/int32_t,因为它们可能等于 protocol::integer
|
||||
assertFalse(transform::is_lsp_basic_type_v<int64_t>,
|
||||
"int64_t不应该是LSP基本类型");
|
||||
assertFalse(transform::is_lsp_basic_type_v<long long>,
|
||||
"long long不应该是LSP基本类型");
|
||||
assertFalse(transform::is_lsp_basic_type_v<float>,
|
||||
"float不应该是LSP基本类型");
|
||||
assertFalse(transform::is_lsp_basic_type_v<std::vector<int>>,
|
||||
"vector不应该是LSP基本类型");
|
||||
assertFalse(transform::is_lsp_basic_type_v<UserStruct>,
|
||||
"UserStruct不应该是LSP基本类型");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== is_lsp_container_type 测试 ====================
|
||||
|
||||
TestResult CommonTests::testIsLSPContainerObject()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
bool is_container = transform::is_lsp_container_type_v<protocol::LSPObject>;
|
||||
assertTrue(is_container, "LSPObject应该是LSP容器类型");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult CommonTests::testIsLSPContainerArray()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
bool is_container = transform::is_lsp_container_type_v<protocol::LSPArray>;
|
||||
assertTrue(is_container, "LSPArray应该是LSP容器类型");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult CommonTests::testIsLSPContainerAny()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
bool is_container = transform::is_lsp_container_type_v<protocol::LSPAny>;
|
||||
assertTrue(is_container, "LSPAny应该是LSP容器类型");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult CommonTests::testIsLSPContainerNegativeCases()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
assertFalse(transform::is_lsp_container_type_v<int>,
|
||||
"int不应该是LSP容器");
|
||||
assertFalse(transform::is_lsp_container_type_v<std::string>,
|
||||
"string不应该是LSP容器");
|
||||
assertFalse(transform::is_lsp_container_type_v<std::vector<int>>,
|
||||
"std::vector不应该是LSP容器");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== is_user_struct 测试 ====================
|
||||
|
||||
TestResult CommonTests::testIsUserStructPositive()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
bool is_user = transform::is_user_struct_v<UserStruct>;
|
||||
assertTrue(is_user, "UserStruct应该被识别为用户结构体");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult CommonTests::testIsUserStructExcludeArithmetic()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
assertFalse(transform::is_user_struct_v<int>,
|
||||
"int不应该是用户结构体");
|
||||
assertFalse(transform::is_user_struct_v<double>,
|
||||
"double不应该是用户结构体");
|
||||
assertFalse(transform::is_user_struct_v<bool>,
|
||||
"bool不应该是用户结构体");
|
||||
assertFalse(transform::is_user_struct_v<char>,
|
||||
"char不应该是用户结构体");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult CommonTests::testIsUserStructExcludeString()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
assertFalse(transform::is_user_struct_v<std::string>,
|
||||
"std::string不应该是用户结构体");
|
||||
assertFalse(transform::is_user_struct_v<protocol::string>,
|
||||
"protocol::string不应该是用户结构体");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult CommonTests::testIsUserStructExcludeLSPTypes()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
assertFalse(transform::is_user_struct_v<protocol::LSPObject>,
|
||||
"LSPObject不应该是用户结构体");
|
||||
assertFalse(transform::is_user_struct_v<protocol::LSPArray>,
|
||||
"LSPArray不应该是用户结构体");
|
||||
assertFalse(transform::is_user_struct_v<protocol::LSPAny>,
|
||||
"LSPAny不应该是用户结构体");
|
||||
assertFalse(transform::is_user_struct_v<protocol::integer>,
|
||||
"protocol::integer不应该是用户结构体");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult CommonTests::testIsUserStructExcludeVector()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
bool is_user = transform::is_user_struct_v<std::vector<int>>;
|
||||
assertFalse(is_user, "std::vector不应该是用户结构体");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult CommonTests::testIsUserStructExcludeMap()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
bool is_user = transform::is_user_struct_v<std::map<std::string, int>>;
|
||||
assertFalse(is_user, "std::map不应该是用户结构体");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult CommonTests::testIsUserStructExcludeOptional()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
bool is_user = transform::is_user_struct_v<std::optional<int>>;
|
||||
assertFalse(is_user, "std::optional不应该是用户结构体");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult CommonTests::testIsUserStructExcludeVariant()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
bool is_user = transform::is_user_struct_v<std::variant<int, std::string>>;
|
||||
assertFalse(is_user, "std::variant不应该是用户结构体");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult CommonTests::testIsUserStructExcludePointers()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
assertFalse(transform::is_user_struct_v<int*>,
|
||||
"int*不应该是用户结构体");
|
||||
assertFalse(transform::is_user_struct_v<const char*>,
|
||||
"const char*不应该是用户结构体");
|
||||
assertFalse(transform::is_user_struct_v<UserStruct*>,
|
||||
"UserStruct*不应该是用户结构体");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace lsp::test
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
#include "test_framework.hpp"
|
||||
|
||||
namespace lsp::test
|
||||
{
|
||||
// Common 类型特征测试类
|
||||
class CommonTests
|
||||
{
|
||||
public:
|
||||
static void registerTests(TestRunner& runner);
|
||||
|
||||
private:
|
||||
// ==================== is_lsp_basic_type 测试 ====================
|
||||
static TestResult testIsLSPBasicInteger();
|
||||
static TestResult testIsLSPBasicUInteger();
|
||||
static TestResult testIsLSPBasicBoolean();
|
||||
static TestResult testIsLSPBasicString();
|
||||
static TestResult testIsLSPBasicDecimal();
|
||||
static TestResult testIsLSPBasicNullptr();
|
||||
static TestResult testIsLSPBasicNegativeCases();
|
||||
|
||||
// ==================== is_lsp_container_type 测试 ====================
|
||||
static TestResult testIsLSPContainerObject();
|
||||
static TestResult testIsLSPContainerArray();
|
||||
static TestResult testIsLSPContainerAny();
|
||||
static TestResult testIsLSPContainerNegativeCases();
|
||||
|
||||
// ==================== is_user_struct 测试 ====================
|
||||
static TestResult testIsUserStructPositive();
|
||||
static TestResult testIsUserStructExcludeArithmetic();
|
||||
static TestResult testIsUserStructExcludeString();
|
||||
static TestResult testIsUserStructExcludeLSPTypes();
|
||||
static TestResult testIsUserStructExcludeVector();
|
||||
static TestResult testIsUserStructExcludeMap();
|
||||
static TestResult testIsUserStructExcludeOptional();
|
||||
static TestResult testIsUserStructExcludeVariant();
|
||||
static TestResult testIsUserStructExcludePointers();
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,389 @@
|
|||
#include <glaze/glaze.hpp>
|
||||
#include "facade_test.hpp"
|
||||
#include "../../src/protocol/transform/facade.hpp"
|
||||
#include "../../src/protocol/detail/basic_types.hpp"
|
||||
|
||||
namespace lsp::test
|
||||
{
|
||||
// 测试用结构体
|
||||
struct TestPerson
|
||||
{
|
||||
std::string name;
|
||||
int age;
|
||||
};
|
||||
|
||||
struct TestConfig
|
||||
{
|
||||
std::string version;
|
||||
bool enabled;
|
||||
std::vector<int> ports;
|
||||
};
|
||||
}
|
||||
|
||||
// Glaze 元数据
|
||||
template<>
|
||||
struct glz::meta<lsp::test::TestPerson>
|
||||
{
|
||||
using T = lsp::test::TestPerson;
|
||||
static constexpr auto value = object(
|
||||
"name", &T::name,
|
||||
"age", &T::age
|
||||
);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct glz::meta<lsp::test::TestConfig>
|
||||
{
|
||||
using T = lsp::test::TestConfig;
|
||||
static constexpr auto value = object(
|
||||
"version", &T::version,
|
||||
"enabled", &T::enabled,
|
||||
"ports", &T::ports
|
||||
);
|
||||
};
|
||||
|
||||
namespace lsp::test
|
||||
{
|
||||
void FacadeTests::registerTests(TestRunner& runner)
|
||||
{
|
||||
// Serialize/Deserialize 测试
|
||||
runner.addTest("Facade - 序列化简单结构体", testSerializeSimpleStruct);
|
||||
runner.addTest("Facade - 序列化复杂结构体", testSerializeComplexStruct);
|
||||
runner.addTest("Facade - 序列化LSPAny", testSerializeLSPAny);
|
||||
runner.addTest("Facade - 反序列化简单结构体", testDeserializeSimpleStruct);
|
||||
runner.addTest("Facade - 反序列化复杂结构体", testDeserializeComplexStruct);
|
||||
runner.addTest("Facade - 反序列化无效JSON", testDeserializeInvalidJSON);
|
||||
runner.addTest("Facade - 序列化反序列化往返", testSerializeDeserializeRoundtrip);
|
||||
|
||||
// check 命名空间测试
|
||||
runner.addTest("Facade - Check IsObject", testCheckIsObject);
|
||||
runner.addTest("Facade - Check IsArray", testCheckIsArray);
|
||||
runner.addTest("Facade - Check IsString", testCheckIsString);
|
||||
runner.addTest("Facade - Check IsNumber", testCheckIsNumber);
|
||||
runner.addTest("Facade - Check IsBool", testCheckIsBool);
|
||||
runner.addTest("Facade - Check IsNull", testCheckIsNull);
|
||||
runner.addTest("Facade - Check 多类型检查", testCheckMultipleTypes);
|
||||
|
||||
// debug 命名空间测试
|
||||
runner.addTest("Facade - Debug GetTypeName", testDebugGetTypeName);
|
||||
runner.addTest("Facade - Debug GetIdString (int)", testDebugGetIdStringInt);
|
||||
runner.addTest("Facade - Debug GetIdString (string)", testDebugGetIdStringString);
|
||||
}
|
||||
|
||||
// ==================== Serialize/Deserialize 测试 ====================
|
||||
|
||||
TestResult FacadeTests::testSerializeSimpleStruct()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
TestPerson person{"Alice", 30};
|
||||
auto json_opt = transform::Serialize(person);
|
||||
|
||||
assertTrue(json_opt.has_value(), "序列化应该成功");
|
||||
const auto& json = json_opt.value();
|
||||
assertTrue(json.find("Alice") != std::string::npos, "JSON应该包含'Alice'");
|
||||
assertTrue(json.find("30") != std::string::npos, "JSON应该包含'30'");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult FacadeTests::testSerializeComplexStruct()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
TestConfig config{"1.0.0", true, {8080, 8081, 8082}};
|
||||
auto json_opt = transform::Serialize(config);
|
||||
|
||||
assertTrue(json_opt.has_value(), "序列化应该成功");
|
||||
const auto& json = json_opt.value();
|
||||
assertTrue(json.find("1.0.0") != std::string::npos, "JSON应该包含版本号");
|
||||
assertTrue(json.find("true") != std::string::npos, "JSON应该包含enabled");
|
||||
assertTrue(json.find("8080") != std::string::npos, "JSON应该包含端口");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult FacadeTests::testSerializeLSPAny()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPObject obj;
|
||||
obj["key"] = protocol::LSPAny(42);
|
||||
obj["name"] = protocol::LSPAny("test");
|
||||
protocol::LSPAny any(obj);
|
||||
|
||||
auto json_opt = transform::Serialize(any);
|
||||
assertTrue(json_opt.has_value(), "序列化LSPAny应该成功");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult FacadeTests::testDeserializeSimpleStruct()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
std::string json = R"({"name":"Bob","age":25})";
|
||||
auto person_opt = transform::Deserialize<TestPerson>(json);
|
||||
|
||||
assertTrue(person_opt.has_value(), "反序列化应该成功");
|
||||
const auto& person = person_opt.value();
|
||||
assertEqual(std::string("Bob"), person.name, "名字应该为'Bob'");
|
||||
assertEqual(25, person.age, "年龄应该为25");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult FacadeTests::testDeserializeComplexStruct()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
std::string json = R"({"version":"2.0.0","enabled":false,"ports":[9000,9001,9002]})";
|
||||
auto config_opt = transform::Deserialize<TestConfig>(json);
|
||||
|
||||
assertTrue(config_opt.has_value(), "反序列化应该成功");
|
||||
const auto& config = config_opt.value();
|
||||
assertEqual(std::string("2.0.0"), config.version, "版本应该为'2.0.0'");
|
||||
assertEqual(false, config.enabled, "enabled应该为false");
|
||||
assertEqual(size_t(3), config.ports.size(), "应该有3个端口");
|
||||
assertEqual(9000, config.ports[0], "第一个端口应该为9000");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult FacadeTests::testDeserializeInvalidJSON()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
std::string json = R"({"invalid json syntax)";
|
||||
auto person_opt = transform::Deserialize<TestPerson>(json);
|
||||
|
||||
assertFalse(person_opt.has_value(), "反序列化无效JSON应该失败");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult FacadeTests::testSerializeDeserializeRoundtrip()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
TestPerson original{"Charlie", 35};
|
||||
|
||||
auto json_opt = transform::Serialize(original);
|
||||
assertTrue(json_opt.has_value(), "序列化应该成功");
|
||||
|
||||
auto person_opt = transform::Deserialize<TestPerson>(json_opt.value());
|
||||
assertTrue(person_opt.has_value(), "反序列化应该成功");
|
||||
|
||||
const auto& restored = person_opt.value();
|
||||
assertEqual(original.name, restored.name, "名字应该保持一致");
|
||||
assertEqual(original.age, restored.age, "年龄应该保持一致");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== check 命名空间测试 ====================
|
||||
|
||||
TestResult FacadeTests::testCheckIsObject()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPObject obj;
|
||||
obj["key"] = protocol::LSPAny(1);
|
||||
protocol::LSPAny any(obj);
|
||||
|
||||
assertTrue(transform::check::IsObject(any), "应该识别为object");
|
||||
|
||||
protocol::LSPAny not_obj(42);
|
||||
assertFalse(transform::check::IsObject(not_obj), "integer不应该识别为object");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult FacadeTests::testCheckIsArray()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPArray arr;
|
||||
arr.push_back(protocol::LSPAny(1));
|
||||
protocol::LSPAny any(arr);
|
||||
|
||||
assertTrue(transform::check::IsArray(any), "应该识别为array");
|
||||
|
||||
protocol::LSPAny not_arr("string");
|
||||
assertFalse(transform::check::IsArray(not_arr), "string不应该识别为array");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult FacadeTests::testCheckIsString()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any("hello");
|
||||
assertTrue(transform::check::IsString(any), "应该识别为string");
|
||||
|
||||
protocol::LSPAny not_str(42);
|
||||
assertFalse(transform::check::IsString(not_str), "integer不应该识别为string");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult FacadeTests::testCheckIsNumber()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny int_any(42);
|
||||
assertTrue(transform::check::IsNumber(int_any), "integer应该是数字");
|
||||
|
||||
protocol::LSPAny uint_any(100u);
|
||||
assertTrue(transform::check::IsNumber(uint_any), "uinteger应该是数字");
|
||||
|
||||
protocol::LSPAny decimal_any(3.14);
|
||||
assertTrue(transform::check::IsNumber(decimal_any), "decimal应该是数字");
|
||||
|
||||
protocol::LSPAny not_num("string");
|
||||
assertFalse(transform::check::IsNumber(not_num), "string不应该是数字");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult FacadeTests::testCheckIsBool()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any(true);
|
||||
assertTrue(transform::check::IsBool(any), "应该识别为bool");
|
||||
|
||||
protocol::LSPAny not_bool(42);
|
||||
assertFalse(transform::check::IsBool(not_bool), "integer不应该识别为bool");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult FacadeTests::testCheckIsNull()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any(nullptr);
|
||||
assertTrue(transform::check::IsNull(any), "应该识别为null");
|
||||
|
||||
protocol::LSPAny not_null(42);
|
||||
assertFalse(transform::check::IsNull(not_null), "integer不应该识别为null");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult FacadeTests::testCheckMultipleTypes()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
// 测试一个值不能同时是多种类型
|
||||
protocol::LSPAny any(42);
|
||||
assertTrue(transform::check::IsNumber(any), "应该是数字");
|
||||
assertFalse(transform::check::IsString(any), "不应该是字符串");
|
||||
assertFalse(transform::check::IsBool(any), "不应该是布尔值");
|
||||
assertFalse(transform::check::IsNull(any), "不应该是null");
|
||||
assertFalse(transform::check::IsArray(any), "不应该是数组");
|
||||
assertFalse(transform::check::IsObject(any), "不应该是对象");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== debug 命名空间测试 ====================
|
||||
|
||||
TestResult FacadeTests::testDebugGetTypeName()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny obj_any(protocol::LSPObject{});
|
||||
assertEqual(std::string("LSPObject"), transform::debug::GetTypeName(obj_any),
|
||||
"类型名应该为'LSPObject'");
|
||||
|
||||
protocol::LSPAny arr_any(protocol::LSPArray{});
|
||||
assertEqual(std::string("LSPArray"), transform::debug::GetTypeName(arr_any),
|
||||
"类型名应该为'LSPArray'");
|
||||
|
||||
protocol::LSPAny str_any("test");
|
||||
assertEqual(std::string("string"), transform::debug::GetTypeName(str_any),
|
||||
"类型名应该为'string'");
|
||||
|
||||
protocol::LSPAny int_any(42);
|
||||
assertEqual(std::string("integer"), transform::debug::GetTypeName(int_any),
|
||||
"类型名应该为'integer'");
|
||||
|
||||
protocol::LSPAny uint_any(100u);
|
||||
assertEqual(std::string("uinteger"), transform::debug::GetTypeName(uint_any),
|
||||
"类型名应该为'uinteger'");
|
||||
|
||||
protocol::LSPAny decimal_any(3.14);
|
||||
assertEqual(std::string("decimal"), transform::debug::GetTypeName(decimal_any),
|
||||
"类型名应该为'decimal'");
|
||||
|
||||
protocol::LSPAny bool_any(true);
|
||||
assertEqual(std::string("boolean"), transform::debug::GetTypeName(bool_any),
|
||||
"类型名应该为'boolean'");
|
||||
|
||||
protocol::LSPAny null_any(nullptr);
|
||||
assertEqual(std::string("null"), transform::debug::GetTypeName(null_any),
|
||||
"类型名应该为'null'");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult FacadeTests::testDebugGetIdStringInt()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
std::variant<int, std::string> id = 123;
|
||||
std::string id_str = transform::debug::GetIdString(id);
|
||||
assertEqual(std::string("123"), id_str, "int id应该转换为'123'");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult FacadeTests::testDebugGetIdStringString()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
std::variant<int, std::string> id = std::string("abc-123-def");
|
||||
std::string id_str = transform::debug::GetIdString(id);
|
||||
assertEqual(std::string("abc-123-def"), id_str, "string id应该保持为'abc-123-def'");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace lsp::test
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
#include "test_framework.hpp"
|
||||
|
||||
namespace lsp::test
|
||||
{
|
||||
// Facade 接口测试类(Serialize, Deserialize, check, debug)
|
||||
class FacadeTests
|
||||
{
|
||||
public:
|
||||
static void registerTests(TestRunner& runner);
|
||||
|
||||
private:
|
||||
// ==================== Serialize/Deserialize 测试 ====================
|
||||
static TestResult testSerializeSimpleStruct();
|
||||
static TestResult testSerializeComplexStruct();
|
||||
static TestResult testSerializeLSPAny();
|
||||
static TestResult testDeserializeSimpleStruct();
|
||||
static TestResult testDeserializeComplexStruct();
|
||||
static TestResult testDeserializeInvalidJSON();
|
||||
static TestResult testSerializeDeserializeRoundtrip();
|
||||
|
||||
// ==================== check 命名空间测试 ====================
|
||||
static TestResult testCheckIsObject();
|
||||
static TestResult testCheckIsArray();
|
||||
static TestResult testCheckIsString();
|
||||
static TestResult testCheckIsNumber();
|
||||
static TestResult testCheckIsBool();
|
||||
static TestResult testCheckIsNull();
|
||||
static TestResult testCheckMultipleTypes();
|
||||
|
||||
// ==================== debug 命名空间测试 ====================
|
||||
static TestResult testDebugGetTypeName();
|
||||
static TestResult testDebugGetIdStringInt();
|
||||
static TestResult testDebugGetIdStringString();
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,689 @@
|
|||
#include <cmath>
|
||||
#include <limits>
|
||||
#include "../../src/protocol/detail/basic_types.hpp"
|
||||
#include "./lsp_any_test.hpp"
|
||||
|
||||
namespace lsp::test
|
||||
{
|
||||
|
||||
// ==================== LSPAnyTests 实现 ====================
|
||||
|
||||
void LSPAnyTests::registerTests(TestRunner& runner)
|
||||
{
|
||||
// 构造函数测试
|
||||
runner.addTest("默认构造函数", testDefaultConstructor);
|
||||
runner.addTest("浮点数构造函数", testDecimalConstructor);
|
||||
runner.addTest("布尔构造函数", testBooleanConstructor);
|
||||
runner.addTest("字符串构造函数", testStringConstructor);
|
||||
runner.addTest("C字符串构造函数", testCStringConstructor);
|
||||
runner.addTest("空指针构造函数", testNullptrConstructor);
|
||||
runner.addTest("LSPObject构造函数", testLSPObjectConstructor);
|
||||
runner.addTest("LSPArray构造函数", testLSPArrayConstructor);
|
||||
|
||||
// 拷贝和移动测试
|
||||
runner.addTest("拷贝构造函数", testCopyConstructor);
|
||||
runner.addTest("移动构造函数", testMoveConstructor);
|
||||
runner.addTest("拷贝赋值", testCopyAssignment);
|
||||
runner.addTest("移动赋值", testMoveAssignment);
|
||||
|
||||
// 赋值操作符测试
|
||||
runner.addTest("浮点数赋值", testDecimalAssignment);
|
||||
runner.addTest("布尔赋值", testBooleanAssignment);
|
||||
runner.addTest("字符串赋值", testStringAssignment);
|
||||
runner.addTest("空指针赋值", testNullptrAssignment);
|
||||
runner.addTest("LSPObject赋值", testLSPObjectAssignment);
|
||||
runner.addTest("LSPArray赋值", testLSPArrayAssignment);
|
||||
|
||||
// 类型检查测试
|
||||
runner.addTest("Is类型检查", testIsMethod);
|
||||
runner.addTest("Get获取值", testGetMethod);
|
||||
|
||||
// 访问者模式测试
|
||||
runner.addTest("Visit访问者", testVisitMethod);
|
||||
|
||||
// 整数模板测试
|
||||
runner.addTest("有符号整数模板", testIntegerTemplateSignedTypes);
|
||||
runner.addTest("无符号整数模板", testIntegerTemplateUnsignedTypes);
|
||||
runner.addTest("整数边界测试", testIntegerTemplateBoundaries);
|
||||
|
||||
// 复杂场景测试
|
||||
runner.addTest("嵌套LSPObject", testNestedLSPObject);
|
||||
runner.addTest("嵌套LSPArray", testNestedLSPArray);
|
||||
runner.addTest("混合嵌套", testMixedNesting);
|
||||
runner.addTest("类型转换", testTypeConversion);
|
||||
|
||||
// 边界情况测试
|
||||
runner.addTest("浮点精度", testFloatPrecision);
|
||||
runner.addTest("大数字", testLargeNumbers);
|
||||
runner.addTest("空容器", testEmptyContainers);
|
||||
}
|
||||
|
||||
// ==================== 构造函数测试 ====================
|
||||
|
||||
TestResult LSPAnyTests::testDefaultConstructor()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any;
|
||||
assertTrue(any.Is<std::nullptr_t>(), "默认构造应该是nullptr类型");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testDecimalConstructor()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::decimal value = 3.14;
|
||||
protocol::LSPAny any(value);
|
||||
|
||||
assertTrue(any.Is<protocol::decimal>(), "应该是decimal类型");
|
||||
assertEqual(value, any.Get<protocol::decimal>(), "值应该相等");
|
||||
|
||||
// 测试 float 构造
|
||||
float fValue = 2.5f;
|
||||
protocol::LSPAny anyFloat(fValue);
|
||||
assertTrue(anyFloat.Is<protocol::decimal>(), "float应该转为decimal类型");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testBooleanConstructor()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny anyTrue(true);
|
||||
protocol::LSPAny anyFalse(false);
|
||||
|
||||
assertTrue(anyTrue.Is<protocol::boolean>(), "应该是boolean类型");
|
||||
assertTrue(anyFalse.Is<protocol::boolean>(), "应该是boolean类型");
|
||||
assertEqual(true, anyTrue.Get<protocol::boolean>(), "值应该是true");
|
||||
assertEqual(false, anyFalse.Get<protocol::boolean>(), "值应该是false");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testStringConstructor()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::string value = "Hello, LSP!";
|
||||
protocol::LSPAny any(value);
|
||||
|
||||
assertTrue(any.Is<protocol::string>(), "应该是string类型");
|
||||
assertEqual(value, any.Get<protocol::string>(), "值应该相等");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testCStringConstructor()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
const char* value = "C String";
|
||||
protocol::LSPAny any(value);
|
||||
|
||||
assertTrue(any.Is<protocol::string>(), "应该是string类型");
|
||||
assertEqual(std::string(value), any.Get<protocol::string>(), "值应该相等");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testNullptrConstructor()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any(nullptr);
|
||||
|
||||
assertTrue(any.Is<std::nullptr_t>(), "应该是nullptr类型");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testLSPObjectConstructor()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPObject obj;
|
||||
obj["key1"] = protocol::LSPAny(42);
|
||||
obj["key2"] = protocol::LSPAny("value");
|
||||
|
||||
protocol::LSPAny any(obj);
|
||||
|
||||
assertTrue(any.Is<protocol::LSPObject>(), "应该是LSPObject类型");
|
||||
const auto& retrievedObj = any.Get<protocol::LSPObject>();
|
||||
assertTrue(retrievedObj.count("key1") > 0, "应该包含key1");
|
||||
assertTrue(retrievedObj.count("key2") > 0, "应该包含key2");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testLSPArrayConstructor()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPArray arr;
|
||||
arr.push_back(protocol::LSPAny(1));
|
||||
arr.push_back(protocol::LSPAny(2));
|
||||
arr.push_back(protocol::LSPAny(3));
|
||||
|
||||
protocol::LSPAny any(arr);
|
||||
|
||||
assertTrue(any.Is<protocol::LSPArray>(), "应该是LSPArray类型");
|
||||
const auto& retrievedArr = any.Get<protocol::LSPArray>();
|
||||
assertTrue(retrievedArr.size() == 3, "数组大小应该是3");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== 拷贝和移动测试 ====================
|
||||
|
||||
TestResult LSPAnyTests::testCopyConstructor()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny original(42);
|
||||
protocol::LSPAny copy(original);
|
||||
|
||||
assertTrue(copy.Is<protocol::integer>(), "拷贝应该保持类型");
|
||||
assertEqual(42, copy.Get<protocol::integer>(), "拷贝应该保持值");
|
||||
assertEqual(42, original.Get<protocol::integer>(), "原值不应改变");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testMoveConstructor()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny original(protocol::string("Move me"));
|
||||
protocol::LSPAny moved(std::move(original));
|
||||
|
||||
assertTrue(moved.Is<protocol::string>(), "移动应该保持类型");
|
||||
assertEqual(std::string("Move me"), moved.Get<protocol::string>(), "移动应该保持值");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testCopyAssignment()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny original(42);
|
||||
protocol::LSPAny target(nullptr);
|
||||
target = original;
|
||||
|
||||
assertTrue(target.Is<protocol::integer>(), "赋值应该保持类型");
|
||||
assertEqual(42, target.Get<protocol::integer>(), "赋值应该保持值");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testMoveAssignment()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny original(protocol::string("Move me"));
|
||||
protocol::LSPAny target(nullptr);
|
||||
target = std::move(original);
|
||||
|
||||
assertTrue(target.Is<protocol::string>(), "移动赋值应该保持类型");
|
||||
assertEqual(std::string("Move me"), target.Get<protocol::string>(), "移动赋值应该保持值");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== 赋值操作符测试 ====================
|
||||
|
||||
TestResult LSPAnyTests::testDecimalAssignment()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any(nullptr);
|
||||
any = 2.718;
|
||||
|
||||
assertTrue(any.Is<protocol::decimal>(), "赋值后应该是decimal类型");
|
||||
assertEqual(2.718, any.Get<protocol::decimal>(), "赋值后值应该正确");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testBooleanAssignment()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any(nullptr);
|
||||
any = true;
|
||||
|
||||
assertTrue(any.Is<protocol::boolean>(), "赋值后应该是boolean类型");
|
||||
assertEqual(true, any.Get<protocol::boolean>(), "赋值后值应该正确");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testStringAssignment()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any(nullptr);
|
||||
any = protocol::string("Assigned");
|
||||
|
||||
assertTrue(any.Is<protocol::string>(), "赋值后应该是string类型");
|
||||
assertEqual(std::string("Assigned"), any.Get<protocol::string>(), "赋值后值应该正确");
|
||||
|
||||
// 测试 C 字符串赋值
|
||||
any = "C String";
|
||||
assertEqual(std::string("C String"), any.Get<protocol::string>(), "C字符串赋值应该正确");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testNullptrAssignment()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any(42);
|
||||
any = nullptr;
|
||||
|
||||
assertTrue(any.Is<std::nullptr_t>(), "赋值后应该是nullptr类型");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testLSPObjectAssignment()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPObject obj;
|
||||
obj["test"] = protocol::LSPAny(999);
|
||||
|
||||
protocol::LSPAny any(nullptr);
|
||||
any = obj;
|
||||
|
||||
assertTrue(any.Is<protocol::LSPObject>(), "赋值后应该是LSPObject类型");
|
||||
const auto& retrievedObj = any.Get<protocol::LSPObject>();
|
||||
assertTrue(retrievedObj.count("test") > 0, "应该包含test键");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testLSPArrayAssignment()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPArray arr;
|
||||
arr.push_back(protocol::LSPAny(10));
|
||||
arr.push_back(protocol::LSPAny(20));
|
||||
|
||||
protocol::LSPAny any(nullptr);
|
||||
any = arr;
|
||||
|
||||
assertTrue(any.Is<protocol::LSPArray>(), "赋值后应该是LSPArray类型");
|
||||
const auto& retrievedArr = any.Get<protocol::LSPArray>();
|
||||
assertTrue(retrievedArr.size() == 2, "数组大小应该是2");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== 类型检查测试 ====================
|
||||
|
||||
TestResult LSPAnyTests::testIsMethod()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny intAny(42);
|
||||
assertTrue(intAny.Is<protocol::integer>(), "Is应该正确识别integer");
|
||||
assertFalse(intAny.Is<protocol::string>(), "Is应该正确排除string");
|
||||
assertFalse(intAny.Is<protocol::decimal>(), "Is应该正确排除decimal");
|
||||
|
||||
protocol::LSPAny strAny(protocol::string("test"));
|
||||
assertTrue(strAny.Is<protocol::string>(), "Is应该正确识别string");
|
||||
assertFalse(strAny.Is<protocol::integer>(), "Is应该正确排除integer");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testGetMethod()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any(42);
|
||||
assertEqual(42, any.Get<protocol::integer>(), "Get应该返回正确的值");
|
||||
|
||||
// 修改值
|
||||
any.Get<protocol::integer>() = 100;
|
||||
assertEqual(100, any.Get<protocol::integer>(), "Get应该返回可修改的引用");
|
||||
|
||||
// const Get 测试
|
||||
const protocol::LSPAny constAny(200);
|
||||
assertEqual(200, constAny.Get<protocol::integer>(), "const Get应该正常工作");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== 访问者模式测试 ====================
|
||||
|
||||
TestResult LSPAnyTests::testVisitMethod()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny intAny(42);
|
||||
protocol::LSPAny strAny(protocol::string("hello"));
|
||||
protocol::LSPAny boolAny(true);
|
||||
|
||||
// 测试 const Visit
|
||||
auto visitor = [](const auto& value) -> std::string {
|
||||
using T = std::decay_t<decltype(value)>;
|
||||
if constexpr (std::is_same_v<T, protocol::integer>)
|
||||
{
|
||||
return "integer:" + std::to_string(value);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, protocol::string>)
|
||||
{
|
||||
return "string:" + value;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, protocol::boolean>)
|
||||
{
|
||||
return value ? "bool:true" : "bool:false";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "other";
|
||||
}
|
||||
};
|
||||
|
||||
std::string result1 = intAny.Visit(visitor);
|
||||
assertEqual(std::string("integer:42"), result1, "Visit应该正确访问integer");
|
||||
|
||||
std::string result2 = strAny.Visit(visitor);
|
||||
assertEqual(std::string("string:hello"), result2, "Visit应该正确访问string");
|
||||
|
||||
std::string result3 = boolAny.Visit(visitor);
|
||||
assertEqual(std::string("bool:true"), result3, "Visit应该正确访问boolean");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== 整数模板测试 ====================
|
||||
|
||||
TestResult LSPAnyTests::testIntegerTemplateSignedTypes()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
// 测试各种有符号整数类型
|
||||
protocol::LSPAny anyInt8(static_cast<int8_t>(-10));
|
||||
assertTrue(anyInt8.Is<protocol::integer>(), "int8_t应该转为integer");
|
||||
assertEqual(-10, anyInt8.Get<protocol::integer>(), "int8_t值应该正确");
|
||||
|
||||
protocol::LSPAny anyInt16(static_cast<int16_t>(-1000));
|
||||
assertTrue(anyInt16.Is<protocol::integer>(), "int16_t应该转为integer");
|
||||
assertEqual(-1000, anyInt16.Get<protocol::integer>(), "int16_t值应该正确");
|
||||
|
||||
protocol::LSPAny anyInt64(static_cast<int64_t>(100000));
|
||||
assertTrue(anyInt64.Is<protocol::integer>(), "int64_t应该转为integer");
|
||||
assertEqual(100000, anyInt64.Get<protocol::integer>(), "int64_t值应该正确");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testIntegerTemplateUnsignedTypes()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
// 测试各种无符号整数类型 - 无符号整数应该存储为uinteger
|
||||
protocol::LSPAny anyUint8(static_cast<uint8_t>(255));
|
||||
assertTrue(anyUint8.Is<protocol::uinteger>(), "uint8_t应该转为uinteger");
|
||||
assertEqual(255u, anyUint8.Get<protocol::uinteger>(), "uint8_t值应该正确");
|
||||
|
||||
protocol::LSPAny anyUint16(static_cast<uint16_t>(5000));
|
||||
assertTrue(anyUint16.Is<protocol::uinteger>(), "uint16_t应该转为uinteger");
|
||||
assertEqual(5000u, anyUint16.Get<protocol::uinteger>(), "uint16_t值应该正确");
|
||||
|
||||
protocol::LSPAny anySize(static_cast<size_t>(12345));
|
||||
assertTrue(anySize.Is<protocol::uinteger>(), "size_t应该转为uinteger");
|
||||
assertEqual(12345u, anySize.Get<protocol::uinteger>(), "size_t值应该正确");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testIntegerTemplateBoundaries()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
// 测试 int32 边界
|
||||
protocol::LSPAny anyMax(std::numeric_limits<int32_t>::max());
|
||||
assertTrue(anyMax.Is<protocol::integer>(), "int32_max应该是integer");
|
||||
assertEqual(std::numeric_limits<int32_t>::max(), anyMax.Get<protocol::integer>(), "int32_max值应该正确");
|
||||
|
||||
protocol::LSPAny anyMin(std::numeric_limits<int32_t>::min());
|
||||
assertTrue(anyMin.Is<protocol::integer>(), "int32_min应该是integer");
|
||||
assertEqual(std::numeric_limits<int32_t>::min(), anyMin.Get<protocol::integer>(), "int32_min值应该正确");
|
||||
|
||||
// 测试超出 int32 范围的 int64
|
||||
int64_t largeValue = static_cast<int64_t>(std::numeric_limits<int32_t>::max()) + 1;
|
||||
protocol::LSPAny anyLarge(largeValue);
|
||||
assertTrue(anyLarge.Is<protocol::decimal>(), "超出int32范围应该转为decimal");
|
||||
|
||||
// 测试 uint32 边界
|
||||
protocol::LSPAny anyUintMax(std::numeric_limits<uint32_t>::max());
|
||||
assertTrue(anyUintMax.Is<protocol::uinteger>(), "uint32_max应该是uinteger");
|
||||
assertEqual(std::numeric_limits<uint32_t>::max(), anyUintMax.Get<protocol::uinteger>(), "uint32_max值应该正确");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== 复杂场景测试 ====================
|
||||
|
||||
TestResult LSPAnyTests::testNestedLSPObject()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPObject innerObj;
|
||||
innerObj["inner_key"] = protocol::LSPAny(42);
|
||||
|
||||
protocol::LSPObject outerObj;
|
||||
outerObj["outer_key"] = protocol::LSPAny(innerObj);
|
||||
outerObj["name"] = protocol::LSPAny(protocol::string("test"));
|
||||
|
||||
protocol::LSPAny any(outerObj);
|
||||
|
||||
assertTrue(any.Is<protocol::LSPObject>(), "应该是LSPObject类型");
|
||||
const auto& obj = any.Get<protocol::LSPObject>();
|
||||
assertTrue(obj.count("outer_key") > 0, "应该包含outer_key");
|
||||
assertTrue(obj.count("name") > 0, "应该包含name");
|
||||
|
||||
const auto& inner = obj.at("outer_key");
|
||||
assertTrue(inner.Is<protocol::LSPObject>(), "嵌套值应该是LSPObject");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testNestedLSPArray()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPArray innerArr;
|
||||
innerArr.push_back(protocol::LSPAny(1));
|
||||
innerArr.push_back(protocol::LSPAny(2));
|
||||
|
||||
protocol::LSPArray outerArr;
|
||||
outerArr.push_back(protocol::LSPAny(innerArr));
|
||||
outerArr.push_back(protocol::LSPAny(protocol::string("text")));
|
||||
|
||||
protocol::LSPAny any(outerArr);
|
||||
|
||||
assertTrue(any.Is<protocol::LSPArray>(), "应该是LSPArray类型");
|
||||
const auto& arr = any.Get<protocol::LSPArray>();
|
||||
assertTrue(arr.size() == 2, "外层数组大小应该是2");
|
||||
assertTrue(arr[0].Is<protocol::LSPArray>(), "第一个元素应该是LSPArray");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testMixedNesting()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPArray arr;
|
||||
arr.push_back(protocol::LSPAny(1));
|
||||
arr.push_back(protocol::LSPAny(protocol::string("two")));
|
||||
arr.push_back(protocol::LSPAny(3.0));
|
||||
|
||||
protocol::LSPObject obj;
|
||||
obj["array"] = protocol::LSPAny(arr);
|
||||
obj["number"] = protocol::LSPAny(42);
|
||||
obj["flag"] = protocol::LSPAny(true);
|
||||
|
||||
protocol::LSPAny any(obj);
|
||||
|
||||
assertTrue(any.Is<protocol::LSPObject>(), "应该是LSPObject类型");
|
||||
const auto& retrievedObj = any.Get<protocol::LSPObject>();
|
||||
assertTrue(retrievedObj.count("array") > 0, "应该包含array");
|
||||
assertTrue(retrievedObj.at("array").Is<protocol::LSPArray>(), "array应该是LSPArray类型");
|
||||
|
||||
const auto& retrievedArr = retrievedObj.at("array").Get<protocol::LSPArray>();
|
||||
assertTrue(retrievedArr.size() == 3, "数组大小应该是3");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testTypeConversion()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any(42);
|
||||
assertTrue(any.Is<protocol::integer>(), "初始应该是integer");
|
||||
|
||||
// 改变类型
|
||||
any = protocol::string("now a string");
|
||||
assertTrue(any.Is<protocol::string>(), "现在应该是string");
|
||||
assertFalse(any.Is<protocol::integer>(), "不应该再是integer");
|
||||
|
||||
// 再次改变
|
||||
any = 3.14;
|
||||
assertTrue(any.Is<protocol::decimal>(), "现在应该是decimal");
|
||||
assertFalse(any.Is<protocol::string>(), "不应该再是string");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== 边界情况测试 ====================
|
||||
|
||||
TestResult LSPAnyTests::testFloatPrecision()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
float fValue = 1.23456789f;
|
||||
protocol::LSPAny anyFloat(fValue);
|
||||
|
||||
assertTrue(anyFloat.Is<protocol::decimal>(), "float应该转为decimal");
|
||||
|
||||
// 注意:float 转为 double 可能有精度变化,这里只是验证类型转换正确
|
||||
protocol::decimal retrieved = anyFloat.Get<protocol::decimal>();
|
||||
assertTrue(std::abs(retrieved - static_cast<double>(fValue)) < 1e-6,
|
||||
"转换后的值应该接近原值");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testLargeNumbers()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
// 测试大的 uint64 值转为 decimal
|
||||
uint64_t largeUint = 10000000000ULL; // 超出 uint32 范围
|
||||
protocol::LSPAny anyLarge(largeUint);
|
||||
|
||||
assertTrue(anyLarge.Is<protocol::decimal>(), "大整数应该转为decimal");
|
||||
|
||||
protocol::decimal retrieved = anyLarge.Get<protocol::decimal>();
|
||||
assertTrue(std::abs(retrieved - static_cast<double>(largeUint)) < 1.0,
|
||||
"转换后的值应该接近原值");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult LSPAnyTests::testEmptyContainers()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPObject emptyObj;
|
||||
protocol::LSPAny anyObj(emptyObj);
|
||||
|
||||
assertTrue(anyObj.Is<protocol::LSPObject>(), "应该是LSPObject类型");
|
||||
const auto& obj = anyObj.Get<protocol::LSPObject>();
|
||||
assertTrue(obj.empty(), "对象应该为空");
|
||||
|
||||
protocol::LSPArray emptyArr;
|
||||
protocol::LSPAny anyArr(emptyArr);
|
||||
|
||||
assertTrue(anyArr.Is<protocol::LSPArray>(), "应该是LSPArray类型");
|
||||
const auto& arr = anyArr.Get<protocol::LSPArray>();
|
||||
assertTrue(arr.empty(), "数组应该为空");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace lsp::test
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
#pragma once
|
||||
#include "./test_framework.hpp"
|
||||
|
||||
namespace lsp::test
|
||||
{
|
||||
// LSPAny 测试类
|
||||
class LSPAnyTests
|
||||
{
|
||||
public:
|
||||
static void registerTests(TestRunner& runner);
|
||||
|
||||
private:
|
||||
// 构造函数测试
|
||||
static TestResult testDefaultConstructor();
|
||||
static TestResult testDecimalConstructor();
|
||||
static TestResult testBooleanConstructor();
|
||||
static TestResult testStringConstructor();
|
||||
static TestResult testCStringConstructor();
|
||||
static TestResult testNullptrConstructor();
|
||||
static TestResult testLSPObjectConstructor();
|
||||
static TestResult testLSPArrayConstructor();
|
||||
|
||||
// 拷贝和移动测试
|
||||
static TestResult testCopyConstructor();
|
||||
static TestResult testMoveConstructor();
|
||||
static TestResult testCopyAssignment();
|
||||
static TestResult testMoveAssignment();
|
||||
|
||||
// 赋值操作符测试
|
||||
static TestResult testIntegerAssignment();
|
||||
static TestResult testUIntegerAssignment();
|
||||
static TestResult testDecimalAssignment();
|
||||
static TestResult testBooleanAssignment();
|
||||
static TestResult testStringAssignment();
|
||||
static TestResult testNullptrAssignment();
|
||||
static TestResult testLSPObjectAssignment();
|
||||
static TestResult testLSPArrayAssignment();
|
||||
|
||||
// 类型检查测试
|
||||
static TestResult testIsMethod();
|
||||
static TestResult testGetMethod();
|
||||
|
||||
// 访问者模式测试
|
||||
static TestResult testVisitMethod();
|
||||
|
||||
// 整数模板测试
|
||||
static TestResult testIntegerTemplateSignedTypes();
|
||||
static TestResult testIntegerTemplateUnsignedTypes();
|
||||
static TestResult testIntegerTemplateBoundaries();
|
||||
|
||||
// 复杂场景测试
|
||||
static TestResult testNestedLSPObject();
|
||||
static TestResult testNestedLSPArray();
|
||||
static TestResult testMixedNesting();
|
||||
static TestResult testTypeConversion();
|
||||
|
||||
// 边界情况测试
|
||||
static TestResult testFloatPrecision();
|
||||
static TestResult testLargeNumbers();
|
||||
static TestResult testEmptyContainers();
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <cmath>
|
||||
#include "./test_framework.hpp"
|
||||
|
||||
namespace lsp::test
|
||||
{
|
||||
// ==================== TestRunner 实现 ====================
|
||||
|
||||
void TestRunner::addTest(const std::string& name, TestFunction test)
|
||||
{
|
||||
tests.push_back({ name, test });
|
||||
}
|
||||
|
||||
void TestRunner::runAllTests()
|
||||
{
|
||||
results.clear();
|
||||
std::cout << "\n========== 开始运行测试 ==========\n"
|
||||
<< std::endl;
|
||||
|
||||
for (const auto& testCase : tests)
|
||||
{
|
||||
try
|
||||
{
|
||||
TestResult result = testCase.function();
|
||||
result.testName = testCase.name;
|
||||
results.push_back(result);
|
||||
printResult(result);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
TestResult result;
|
||||
result.testName = testCase.name;
|
||||
result.passed = false;
|
||||
result.message = std::string("异常: ") + e.what();
|
||||
results.push_back(result);
|
||||
printResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
printSummary();
|
||||
}
|
||||
|
||||
int TestRunner::getFailedCount() const
|
||||
{
|
||||
int count = 0;
|
||||
for (const auto& result : results)
|
||||
{
|
||||
if (!result.passed)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int TestRunner::getTotalCount() const
|
||||
{
|
||||
return static_cast<int>(results.size());
|
||||
}
|
||||
|
||||
void TestRunner::printResult(const TestResult& result)
|
||||
{
|
||||
if (result.passed)
|
||||
{
|
||||
std::cout << "[✓] " << result.testName << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "[✗] " << result.testName << std::endl;
|
||||
std::cout << " 失败: " << result.message << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void TestRunner::printSummary()
|
||||
{
|
||||
int passed = getTotalCount() - getFailedCount();
|
||||
int failed = getFailedCount();
|
||||
int total = getTotalCount();
|
||||
|
||||
std::cout << "\n========== 测试总结 ==========\n";
|
||||
std::cout << "总计: " << total << " 个测试\n";
|
||||
std::cout << "通过: " << passed << " 个\n";
|
||||
std::cout << "失败: " << failed << " 个\n";
|
||||
|
||||
if (failed == 0)
|
||||
{
|
||||
std::cout << "\n所有测试通过! ✓\n"
|
||||
<< std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "\n部分测试失败 ✗\n"
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 断言辅助函数实现 ====================
|
||||
|
||||
void assertTrue(bool condition, const std::string& message)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
}
|
||||
|
||||
void assertFalse(bool condition, const std::string& message)
|
||||
{
|
||||
if (condition)
|
||||
{
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
}
|
||||
|
||||
void assertEqual(int expected, int actual, const std::string& message)
|
||||
{
|
||||
if (expected != actual)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << message << " (期望: " << expected << ", 实际: " << actual << ")";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
void assertEqual(unsigned int expected, unsigned int actual, const std::string& message)
|
||||
{
|
||||
if (expected != actual)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << message << " (期望: " << expected << ", 实际: " << actual << ")";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
void assertEqual(double expected, double actual, const std::string& message)
|
||||
{
|
||||
if (std::abs(expected - actual) > 1e-9)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << message << " (期望: " << expected << ", 实际: " << actual << ")";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
void assertEqual(bool expected, bool actual, const std::string& message)
|
||||
{
|
||||
if (expected != actual)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << message << " (期望: " << (expected ? "true" : "false")
|
||||
<< ", 实际: " << (actual ? "true" : "false") << ")";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
void assertEqual(const std::string& expected, const std::string& actual, const std::string& message)
|
||||
{
|
||||
if (expected != actual)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << message << " (期望: \"" << expected << "\", 实际: \"" << actual << "\")";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
void assertEqual(size_t expected, size_t actual, const std::string& message)
|
||||
{
|
||||
if (expected != actual)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << message << " (期望: " << expected << ", 实际: " << actual << ")";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lsp::test
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace lsp::test
|
||||
{
|
||||
// 简单的测试结果结构
|
||||
struct TestResult
|
||||
{
|
||||
std::string testName;
|
||||
bool passed;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
// 测试运行器类
|
||||
class TestRunner
|
||||
{
|
||||
public:
|
||||
using TestFunction = std::function<TestResult()>;
|
||||
|
||||
void addTest(const std::string& name, TestFunction test);
|
||||
void runAllTests();
|
||||
int getFailedCount() const;
|
||||
int getTotalCount() const;
|
||||
|
||||
private:
|
||||
struct TestCase
|
||||
{
|
||||
std::string name;
|
||||
TestFunction function;
|
||||
};
|
||||
|
||||
std::vector<TestCase> tests;
|
||||
std::vector<TestResult> results;
|
||||
|
||||
void printResult(const TestResult& result);
|
||||
void printSummary();
|
||||
};
|
||||
|
||||
// 断言辅助函数
|
||||
void assertTrue(bool condition, const std::string& message);
|
||||
void assertFalse(bool condition, const std::string& message);
|
||||
void assertEqual(int expected, int actual, const std::string& message);
|
||||
void assertEqual(unsigned int expected, unsigned int actual, const std::string& message);
|
||||
void assertEqual(double expected, double actual, const std::string& message);
|
||||
void assertEqual(bool expected, bool actual, const std::string& message);
|
||||
void assertEqual(const std::string& expected, const std::string& actual, const std::string& message);
|
||||
void assertEqual(size_t expected, size_t actual, const std::string& message);
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
#include <iostream>
|
||||
#include "test_framework.hpp"
|
||||
#include "lsp_any_test.hpp"
|
||||
#include "transformer_test.hpp"
|
||||
#include "facade_test.hpp"
|
||||
#include "common_test.hpp"
|
||||
|
||||
int main()
|
||||
{
|
||||
lsp::test::TestRunner runner;
|
||||
|
||||
std::cout << "\n========================================" << std::endl;
|
||||
std::cout << " LSP Transform 库单元测试套件" << std::endl;
|
||||
std::cout << "========================================\n" << std::endl;
|
||||
|
||||
// 注册所有测试
|
||||
std::cout << "正在注册测试..." << std::endl;
|
||||
|
||||
std::cout << " - LSPAny 基本功能测试" << std::endl;
|
||||
lsp::test::LSPAnyTests::registerTests(runner);
|
||||
|
||||
std::cout << " - Transformer 转换功能测试" << std::endl;
|
||||
lsp::test::TransformerTests::registerTests(runner);
|
||||
|
||||
std::cout << " - Facade 接口测试" << std::endl;
|
||||
lsp::test::FacadeTests::registerTests(runner);
|
||||
|
||||
std::cout << " - Common 类型特征测试" << std::endl;
|
||||
lsp::test::CommonTests::registerTests(runner);
|
||||
|
||||
// 运行所有测试
|
||||
runner.runAllTests();
|
||||
|
||||
// 返回失败的测试数量(用于CI/CD)
|
||||
return runner.getFailedCount();
|
||||
}
|
||||
|
|
@ -0,0 +1,654 @@
|
|||
#include "transformer_test.hpp"
|
||||
#include "../../src/protocol/transform/transformer.hpp"
|
||||
#include "../../src/protocol/transform/common.hpp"
|
||||
#include "../../src/protocol/detail/basic_types.hpp"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
|
||||
namespace lsp::test
|
||||
{
|
||||
void TransformerTests::registerTests(TestRunner& runner)
|
||||
{
|
||||
// ToLSPAny 基本类型
|
||||
runner.addTest("Transformer - ToLSPAny Boolean", testToLSPAnyBoolean);
|
||||
runner.addTest("Transformer - ToLSPAny Integer", testToLSPAnyInteger);
|
||||
runner.addTest("Transformer - ToLSPAny UInteger", testToLSPAnyUInteger);
|
||||
runner.addTest("Transformer - ToLSPAny Float", testToLSPAnyFloat);
|
||||
runner.addTest("Transformer - ToLSPAny Double", testToLSPAnyDouble);
|
||||
runner.addTest("Transformer - ToLSPAny String", testToLSPAnyString);
|
||||
runner.addTest("Transformer - ToLSPAny CString", testToLSPAnyCString);
|
||||
|
||||
// ToLSPAny 容器类型
|
||||
runner.addTest("Transformer - ToLSPAny Vector", testToLSPAnyVector);
|
||||
runner.addTest("Transformer - ToLSPAny Map", testToLSPAnyMap);
|
||||
runner.addTest("Transformer - ToLSPAny Optional (有值)", testToLSPAnyOptionalValue);
|
||||
runner.addTest("Transformer - ToLSPAny Optional (nullopt)", testToLSPAnyOptionalNullopt);
|
||||
|
||||
// ToLSPAny LSP类型
|
||||
runner.addTest("Transformer - ToLSPAny LSPObject", testToLSPAnyLSPObject);
|
||||
runner.addTest("Transformer - ToLSPAny LSPArray", testToLSPAnyLSPArray);
|
||||
runner.addTest("Transformer - ToLSPAny LSPAny", testToLSPAnyLSPAny);
|
||||
|
||||
// FromLSPAny 基本类型
|
||||
runner.addTest("Transformer - FromLSPAny Boolean", testFromLSPAnyBoolean);
|
||||
runner.addTest("Transformer - FromLSPAny Integer", testFromLSPAnyInteger);
|
||||
runner.addTest("Transformer - FromLSPAny String", testFromLSPAnyString);
|
||||
runner.addTest("Transformer - FromLSPAny Double", testFromLSPAnyDouble);
|
||||
|
||||
// FromLSPAny 容器类型
|
||||
runner.addTest("Transformer - FromLSPAny Vector", testFromLSPAnyVector);
|
||||
runner.addTest("Transformer - FromLSPAny Optional (有值)", testFromLSPAnyOptionalValue);
|
||||
runner.addTest("Transformer - FromLSPAny Optional (null)", testFromLSPAnyOptionalNull);
|
||||
runner.addTest("Transformer - FromLSPAny LSPObject", testFromLSPAnyLSPObject);
|
||||
runner.addTest("Transformer - FromLSPAny LSPArray", testFromLSPAnyLSPArray);
|
||||
|
||||
// 数字类型转换
|
||||
runner.addTest("Transformer - 提取Integer", testExtractNumberFromInteger);
|
||||
runner.addTest("Transformer - 提取UInteger", testExtractNumberFromUInteger);
|
||||
runner.addTest("Transformer - 提取Decimal", testExtractNumberFromDecimal);
|
||||
runner.addTest("Transformer - 跨类型数字提取", testExtractNumberCrossType);
|
||||
|
||||
// 错误处理
|
||||
runner.addTest("Transformer - 类型不匹配错误", testFromLSPAnyTypeMismatch);
|
||||
runner.addTest("Transformer - 无效数字错误", testFromLSPAnyInvalidNumber);
|
||||
runner.addTest("Transformer - 无效数组错误", testFromLSPAnyInvalidArray);
|
||||
|
||||
// 嵌套结构
|
||||
runner.addTest("Transformer - 嵌套Vector", testNestedVector);
|
||||
runner.addTest("Transformer - 嵌套LSPObject", testNestedLSPObject);
|
||||
runner.addTest("Transformer - 混合类型嵌套", testMixedTypeNesting);
|
||||
}
|
||||
|
||||
// ==================== ToLSPAny 基本类型测试 ====================
|
||||
|
||||
TestResult TransformerTests::testToLSPAnyBoolean()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
auto any = transform::LSPAnyConverter::ToLSPAny(true);
|
||||
assertTrue(any.Is<protocol::boolean>(), "应该转换为boolean类型");
|
||||
assertEqual(true, any.Get<protocol::boolean>(), "值应该为true");
|
||||
|
||||
auto any_false = transform::LSPAnyConverter::ToLSPAny(false);
|
||||
assertEqual(false, any_false.Get<protocol::boolean>(), "值应该为false");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testToLSPAnyInteger()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
auto any = transform::LSPAnyConverter::ToLSPAny(42);
|
||||
assertTrue(any.Is<protocol::integer>(), "应该转换为integer类型");
|
||||
assertEqual(42, any.Get<protocol::integer>(), "值应该为42");
|
||||
|
||||
auto any_negative = transform::LSPAnyConverter::ToLSPAny(-100);
|
||||
assertEqual(-100, any_negative.Get<protocol::integer>(), "负数应该正确转换");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testToLSPAnyUInteger()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
auto any = transform::LSPAnyConverter::ToLSPAny(42u);
|
||||
assertTrue(any.Is<protocol::uinteger>(), "应该转换为uinteger类型");
|
||||
assertEqual(42u, any.Get<protocol::uinteger>(), "值应该为42");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testToLSPAnyFloat()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
auto any = transform::LSPAnyConverter::ToLSPAny(3.14f);
|
||||
assertTrue(any.Is<protocol::decimal>(), "float应该转换为decimal类型");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testToLSPAnyDouble()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
auto any = transform::LSPAnyConverter::ToLSPAny(2.718);
|
||||
assertTrue(any.Is<protocol::decimal>(), "应该转换为decimal类型");
|
||||
assertEqual(2.718, any.Get<protocol::decimal>(), "值应该为2.718");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testToLSPAnyString()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
std::string str = "hello world";
|
||||
auto any = transform::LSPAnyConverter::ToLSPAny(str);
|
||||
assertTrue(any.Is<protocol::string>(), "应该转换为string类型");
|
||||
assertEqual(str, any.Get<protocol::string>(), "值应该为'hello world'");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testToLSPAnyCString()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
auto any = transform::LSPAnyConverter::ToLSPAny("test");
|
||||
assertTrue(any.Is<protocol::string>(), "C字符串应该转换为string类型");
|
||||
assertEqual(std::string("test"), any.Get<protocol::string>(), "值应该为'test'");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== ToLSPAny 容器类型测试 ====================
|
||||
|
||||
TestResult TransformerTests::testToLSPAnyVector()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
std::vector<int> vec = { 1, 2, 3, 4, 5 };
|
||||
auto any = transform::LSPAnyConverter::ToLSPAny(vec);
|
||||
|
||||
assertTrue(any.Is<protocol::LSPArray>(), "应该转换为LSPArray类型");
|
||||
const auto& arr = any.Get<protocol::LSPArray>();
|
||||
assertEqual(size_t(5), arr.size(), "数组大小应该为5");
|
||||
assertTrue(arr[0].Is<protocol::integer>(), "元素应该是integer类型");
|
||||
assertEqual(1, arr[0].Get<protocol::integer>(), "第一个元素应该为1");
|
||||
assertEqual(5, arr[4].Get<protocol::integer>(), "最后一个元素应该为5");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testToLSPAnyMap()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
std::map<std::string, int> map = { { "a", 1 }, { "b", 2 }, { "c", 3 } };
|
||||
auto any = transform::LSPAnyConverter::ToLSPAny(map);
|
||||
|
||||
assertTrue(any.Is<protocol::LSPObject>(), "应该转换为LSPObject类型");
|
||||
const auto& obj = any.Get<protocol::LSPObject>();
|
||||
assertEqual(size_t(3), obj.size(), "对象大小应该为3");
|
||||
assertTrue(obj.count("a") > 0, "应该包含键'a'");
|
||||
assertEqual(1, obj.at("a").Get<protocol::integer>(), "键'a'的值应该为1");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testToLSPAnyOptionalValue()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
std::optional<int> opt = 100;
|
||||
auto any = transform::LSPAnyConverter::ToLSPAny(opt);
|
||||
|
||||
assertTrue(any.Is<protocol::integer>(), "有值的optional应该转换为对应类型");
|
||||
assertEqual(100, any.Get<protocol::integer>(), "值应该为100");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testToLSPAnyOptionalNullopt()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
std::optional<int> opt = std::nullopt;
|
||||
auto any = transform::LSPAnyConverter::ToLSPAny(opt);
|
||||
|
||||
assertTrue(any.Is<std::nullptr_t>(), "nullopt应该转换为null");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== ToLSPAny LSP类型测试 ====================
|
||||
|
||||
TestResult TransformerTests::testToLSPAnyLSPObject()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPObject obj;
|
||||
obj["key"] = protocol::LSPAny(42);
|
||||
auto any = transform::LSPAnyConverter::ToLSPAny(obj);
|
||||
|
||||
assertTrue(any.Is<protocol::LSPObject>(), "应该保持LSPObject类型");
|
||||
const auto& retrieved = any.Get<protocol::LSPObject>();
|
||||
assertEqual(size_t(1), retrieved.size(), "对象应该包含1个元素");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testToLSPAnyLSPArray()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPArray arr;
|
||||
arr.push_back(protocol::LSPAny(1));
|
||||
arr.push_back(protocol::LSPAny(2));
|
||||
auto any = transform::LSPAnyConverter::ToLSPAny(arr);
|
||||
|
||||
assertTrue(any.Is<protocol::LSPArray>(), "应该保持LSPArray类型");
|
||||
const auto& retrieved = any.Get<protocol::LSPArray>();
|
||||
assertEqual(size_t(2), retrieved.size(), "数组应该包含2个元素");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testToLSPAnyLSPAny()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny original(42);
|
||||
auto any = transform::LSPAnyConverter::ToLSPAny(original);
|
||||
|
||||
assertTrue(any.Is<protocol::integer>(), "应该保持类型");
|
||||
assertEqual(42, any.Get<protocol::integer>(), "应该保持值");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== FromLSPAny 基本类型测试 ====================
|
||||
|
||||
TestResult TransformerTests::testFromLSPAnyBoolean()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any(true);
|
||||
bool value = transform::LSPAnyConverter::FromLSPAny<bool>(any);
|
||||
assertEqual(true, value, "应该提取出true");
|
||||
|
||||
protocol::LSPAny any_false(false);
|
||||
bool value_false = transform::LSPAnyConverter::FromLSPAny<bool>(any_false);
|
||||
assertEqual(false, value_false, "应该提取出false");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testFromLSPAnyInteger()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any(42);
|
||||
int value = transform::LSPAnyConverter::FromLSPAny<int>(any);
|
||||
assertEqual(42, value, "应该提取出42");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testFromLSPAnyString()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any("test string");
|
||||
std::string value = transform::LSPAnyConverter::FromLSPAny<std::string>(any);
|
||||
assertEqual(std::string("test string"), value, "应该提取出'test string'");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testFromLSPAnyDouble()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any(3.14159);
|
||||
double value = transform::LSPAnyConverter::FromLSPAny<double>(any);
|
||||
assertEqual(3.14159, value, "应该提取出3.14159");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== FromLSPAny 容器类型测试 ====================
|
||||
|
||||
TestResult TransformerTests::testFromLSPAnyVector()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPArray arr;
|
||||
arr.push_back(protocol::LSPAny(10));
|
||||
arr.push_back(protocol::LSPAny(20));
|
||||
arr.push_back(protocol::LSPAny(30));
|
||||
protocol::LSPAny any(arr);
|
||||
|
||||
auto vec = transform::LSPAnyConverter::FromLSPAny<std::vector<int>>(any);
|
||||
assertEqual(size_t(3), vec.size(), "vector大小应该为3");
|
||||
assertEqual(10, vec[0], "第一个元素应该为10");
|
||||
assertEqual(20, vec[1], "第二个元素应该为20");
|
||||
assertEqual(30, vec[2], "第三个元素应该为30");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testFromLSPAnyOptionalValue()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any(42);
|
||||
auto opt = transform::LSPAnyConverter::FromLSPAny<std::optional<int>>(any);
|
||||
|
||||
assertTrue(opt.has_value(), "optional应该有值");
|
||||
assertEqual(42, opt.value(), "值应该为42");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testFromLSPAnyOptionalNull()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any(nullptr);
|
||||
auto opt = transform::LSPAnyConverter::FromLSPAny<std::optional<int>>(any);
|
||||
|
||||
assertFalse(opt.has_value(), "optional应该为nullopt");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testFromLSPAnyLSPObject()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPObject obj;
|
||||
obj["key1"] = protocol::LSPAny(100);
|
||||
obj["key2"] = protocol::LSPAny("value");
|
||||
protocol::LSPAny any(obj);
|
||||
|
||||
auto retrieved = transform::LSPAnyConverter::FromLSPAny<protocol::LSPObject>(any);
|
||||
assertEqual(size_t(2), retrieved.size(), "对象应该有2个键");
|
||||
assertTrue(retrieved.count("key1") > 0, "应该包含key1");
|
||||
assertTrue(retrieved.count("key2") > 0, "应该包含key2");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testFromLSPAnyLSPArray()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPArray arr;
|
||||
arr.push_back(protocol::LSPAny(1));
|
||||
arr.push_back(protocol::LSPAny(2));
|
||||
protocol::LSPAny any(arr);
|
||||
|
||||
auto retrieved = transform::LSPAnyConverter::FromLSPAny<protocol::LSPArray>(any);
|
||||
assertEqual(size_t(2), retrieved.size(), "数组应该有2个元素");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== 数字类型转换测试 ====================
|
||||
|
||||
TestResult TransformerTests::testExtractNumberFromInteger()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any(static_cast<protocol::integer>(42));
|
||||
int value = transform::LSPAnyConverter::FromLSPAny<int>(any);
|
||||
assertEqual(42, value, "应该从integer提取出42");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testExtractNumberFromUInteger()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any(static_cast<protocol::uinteger>(100));
|
||||
unsigned int value = transform::LSPAnyConverter::FromLSPAny<unsigned int>(any);
|
||||
assertEqual(100u, value, "应该从uinteger提取出100");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testExtractNumberFromDecimal()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any(static_cast<protocol::decimal>(3.14));
|
||||
double value = transform::LSPAnyConverter::FromLSPAny<double>(any);
|
||||
assertEqual(3.14, value, "应该从decimal提取出3.14");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testExtractNumberCrossType()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
// integer -> double
|
||||
protocol::LSPAny int_any(42);
|
||||
double double_value = transform::LSPAnyConverter::FromLSPAny<double>(int_any);
|
||||
assertEqual(42.0, double_value, "integer应该能转换为double");
|
||||
|
||||
// uinteger -> int
|
||||
protocol::LSPAny uint_any(100u);
|
||||
int int_value = transform::LSPAnyConverter::FromLSPAny<int>(uint_any);
|
||||
assertEqual(100, int_value, "uinteger应该能转换为int");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== 错误处理测试 ====================
|
||||
|
||||
TestResult TransformerTests::testFromLSPAnyTypeMismatch()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any(42);
|
||||
|
||||
try
|
||||
{
|
||||
// 尝试将integer提取为string应该抛出异常
|
||||
(void)transform::LSPAnyConverter::FromLSPAny<std::string>(any);
|
||||
result.passed = false;
|
||||
result.message = "应该抛出ConversionError异常";
|
||||
}
|
||||
catch (const transform::ConversionError& e)
|
||||
{
|
||||
// 预期的异常
|
||||
result.message = "成功";
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
result.passed = false;
|
||||
result.message = "抛出了错误的异常类型";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testFromLSPAnyInvalidNumber()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any("not a number");
|
||||
|
||||
try
|
||||
{
|
||||
// 尝试将string提取为int应该抛出异常
|
||||
(void)transform::LSPAnyConverter::FromLSPAny<int>(any);
|
||||
result.passed = false;
|
||||
result.message = "应该抛出ConversionError异常";
|
||||
}
|
||||
catch (const transform::ConversionError& e)
|
||||
{
|
||||
// 预期的异常
|
||||
result.message = "成功";
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
result.passed = false;
|
||||
result.message = "抛出了错误的异常类型";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testFromLSPAnyInvalidArray()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
protocol::LSPAny any(42);
|
||||
|
||||
try
|
||||
{
|
||||
// 尝试将integer提取为vector应该抛出异常
|
||||
(void)transform::LSPAnyConverter::FromLSPAny<std::vector<int>>(any);
|
||||
result.passed = false;
|
||||
result.message = "应该抛出ConversionError异常";
|
||||
}
|
||||
catch (const transform::ConversionError& e)
|
||||
{
|
||||
// 预期的异常
|
||||
result.message = "成功";
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
result.passed = false;
|
||||
result.message = "抛出了错误的异常类型";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== 嵌套结构测试 ====================
|
||||
|
||||
TestResult TransformerTests::testNestedVector()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
std::vector<std::vector<int>> nested = { { 1, 2 }, { 3, 4, 5 } };
|
||||
auto any = transform::LSPAnyConverter::ToLSPAny(nested);
|
||||
|
||||
assertTrue(any.Is<protocol::LSPArray>(), "应该转换为LSPArray");
|
||||
const auto& arr = any.Get<protocol::LSPArray>();
|
||||
assertEqual(size_t(2), arr.size(), "外层数组应该有2个元素");
|
||||
assertTrue(arr[0].Is<protocol::LSPArray>(), "第一个元素应该是LSPArray");
|
||||
|
||||
auto result_vec = transform::LSPAnyConverter::FromLSPAny<std::vector<std::vector<int>>>(any);
|
||||
assertEqual(size_t(2), result_vec.size(), "应该有2个子vector");
|
||||
assertEqual(size_t(2), result_vec[0].size(), "第一个子vector应该有2个元素");
|
||||
assertEqual(1, result_vec[0][0], "第一个元素应该为1");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testNestedLSPObject()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
// 直接使用 LSPObject 创建嵌套结构:{"outer": {"inner": 42}}
|
||||
protocol::LSPObject innerObj;
|
||||
innerObj["inner"] = protocol::LSPAny(42);
|
||||
|
||||
protocol::LSPObject outerObj;
|
||||
outerObj["outer"] = protocol::LSPAny(innerObj);
|
||||
|
||||
auto any = transform::LSPAnyConverter::ToLSPAny(outerObj);
|
||||
assertTrue(any.Is<protocol::LSPObject>(), "应该保持为LSPObject");
|
||||
|
||||
// 提取为 LSPObject,然后手动检查嵌套值
|
||||
auto result_obj = transform::LSPAnyConverter::FromLSPAny<protocol::LSPObject>(any);
|
||||
assertTrue(result_obj.count("outer") > 0, "应该包含'outer'键");
|
||||
assertTrue(result_obj.at("outer").Is<protocol::LSPObject>(), "'outer'应该是LSPObject");
|
||||
|
||||
const auto& inner_obj = result_obj.at("outer").Get<protocol::LSPObject>();
|
||||
assertTrue(inner_obj.count("inner") > 0, "应该包含'inner'键");
|
||||
assertEqual(42, inner_obj.at("inner").Get<protocol::integer>(), "嵌套值应该为42");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
TestResult TransformerTests::testMixedTypeNesting()
|
||||
{
|
||||
TestResult result;
|
||||
result.passed = true;
|
||||
|
||||
// 直接使用 LSPArray 和 LSPObject 创建混合嵌套结构
|
||||
protocol::LSPArray arr;
|
||||
arr.push_back(protocol::LSPAny(1));
|
||||
arr.push_back(protocol::LSPAny(nullptr)); // 表示 nullopt
|
||||
arr.push_back(protocol::LSPAny(3));
|
||||
|
||||
protocol::LSPObject obj;
|
||||
obj["data"] = protocol::LSPAny(arr);
|
||||
|
||||
auto any = transform::LSPAnyConverter::ToLSPAny(obj);
|
||||
assertTrue(any.Is<protocol::LSPObject>(), "应该保持为LSPObject");
|
||||
|
||||
const auto& retrieved_obj = any.Get<protocol::LSPObject>();
|
||||
const auto& retrieved_arr = retrieved_obj.at("data").Get<protocol::LSPArray>();
|
||||
assertEqual(size_t(3), retrieved_arr.size(), "数组应该有3个元素");
|
||||
assertTrue(retrieved_arr[0].Is<protocol::integer>(), "第一个元素应该是integer");
|
||||
assertTrue(retrieved_arr[1].Is<std::nullptr_t>(), "第二个元素应该是null");
|
||||
assertTrue(retrieved_arr[2].Is<protocol::integer>(), "第三个元素应该是integer");
|
||||
|
||||
result.message = "成功";
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace lsp::test
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
#pragma once
|
||||
#include "test_framework.hpp"
|
||||
|
||||
namespace lsp::test
|
||||
{
|
||||
// Transformer 转换功能测试类
|
||||
class TransformerTests
|
||||
{
|
||||
public:
|
||||
static void registerTests(TestRunner& runner);
|
||||
|
||||
private:
|
||||
// ==================== ToLSPAny 基本类型测试 ====================
|
||||
static TestResult testToLSPAnyBoolean();
|
||||
static TestResult testToLSPAnyInteger();
|
||||
static TestResult testToLSPAnyUInteger();
|
||||
static TestResult testToLSPAnyFloat();
|
||||
static TestResult testToLSPAnyDouble();
|
||||
static TestResult testToLSPAnyString();
|
||||
static TestResult testToLSPAnyCString();
|
||||
|
||||
// ==================== ToLSPAny 容器类型测试 ====================
|
||||
static TestResult testToLSPAnyVector();
|
||||
static TestResult testToLSPAnyMap();
|
||||
static TestResult testToLSPAnyOptionalValue();
|
||||
static TestResult testToLSPAnyOptionalNullopt();
|
||||
|
||||
// ==================== ToLSPAny LSP类型测试 ====================
|
||||
static TestResult testToLSPAnyLSPObject();
|
||||
static TestResult testToLSPAnyLSPArray();
|
||||
static TestResult testToLSPAnyLSPAny();
|
||||
|
||||
// ==================== FromLSPAny 基本类型测试 ====================
|
||||
static TestResult testFromLSPAnyBoolean();
|
||||
static TestResult testFromLSPAnyInteger();
|
||||
static TestResult testFromLSPAnyString();
|
||||
static TestResult testFromLSPAnyDouble();
|
||||
|
||||
// ==================== FromLSPAny 容器类型测试 ====================
|
||||
static TestResult testFromLSPAnyVector();
|
||||
static TestResult testFromLSPAnyOptionalValue();
|
||||
static TestResult testFromLSPAnyOptionalNull();
|
||||
static TestResult testFromLSPAnyLSPObject();
|
||||
static TestResult testFromLSPAnyLSPArray();
|
||||
|
||||
// ==================== 数字类型转换测试 ====================
|
||||
static TestResult testExtractNumberFromInteger();
|
||||
static TestResult testExtractNumberFromUInteger();
|
||||
static TestResult testExtractNumberFromDecimal();
|
||||
static TestResult testExtractNumberCrossType();
|
||||
|
||||
// ==================== 错误处理测试 ====================
|
||||
static TestResult testFromLSPAnyTypeMismatch();
|
||||
static TestResult testFromLSPAnyInvalidNumber();
|
||||
static TestResult testFromLSPAnyInvalidArray();
|
||||
|
||||
// ==================== 嵌套结构测试 ====================
|
||||
static TestResult testNestedVector();
|
||||
static TestResult testNestedLSPObject();
|
||||
static TestResult testMixedTypeNesting();
|
||||
};
|
||||
}
|
||||
Loading…
Reference in New Issue