use proc_macro2::TokenStream as TokenStream2;
use syn::{Field, Type};
use crate::common::{MacroError, MacroResult, utils};
use super::type_converter::TypeConverter;
#[derive(Debug, Clone)]
pub struct StringConverter;
impl StringConverter {
pub fn new() -> Self {
Self
}
fn is_string_type(
&self,
field_type: &Type,
) -> bool {
if utils::is_option_type(field_type) {
if let Some(inner_type) =
utils::extract_option_inner_type(field_type)
{
self.is_basic_string_type(inner_type)
} else {
false
}
} else {
self.is_basic_string_type(field_type)
}
}
fn is_basic_string_type(
&self,
field_type: &Type,
) -> bool {
let type_name = utils::extract_type_name(field_type);
matches!(type_name.as_str(), "String" | "str" | "&str")
}
}
impl Default for StringConverter {
fn default() -> Self {
Self::new()
}
}
impl TypeConverter for StringConverter {
fn convert_field_to_json_value(
&self,
field: &Field,
) -> MacroResult<TokenStream2> {
if !self.supports_type(&field.ty) {
let type_name = utils::extract_type_name(&field.ty);
let field_name = field
.ident
.as_ref()
.map(|i| i.to_string())
.unwrap_or_else(|| "匿名字段".to_string());
return Err(MacroError::unsupported_field_type(
&field_name,
&type_name,
field,
));
}
let field_name = field.ident.as_ref().ok_or_else(|| {
MacroError::parse_error("字段缺少名称(不支持匿名字段)", field)
})?;
let conversion_code =
utils::generate_field_conversion(field_name, &field.ty);
Ok(conversion_code)
}
fn supports_type(
&self,
field_type: &Type,
) -> bool {
self.is_string_type(field_type)
}
fn priority(&self) -> i32 {
90 }
fn name(&self) -> &'static str {
"StringConverter"
}
}
#[derive(Debug, Clone)]
pub struct NumericConverter;
impl NumericConverter {
pub fn new() -> Self {
Self
}
fn is_numeric_type(
&self,
field_type: &Type,
) -> bool {
if utils::is_option_type(field_type) {
if let Some(inner_type) =
utils::extract_option_inner_type(field_type)
{
self.is_basic_numeric_type(inner_type)
} else {
false
}
} else {
self.is_basic_numeric_type(field_type)
}
}
fn is_basic_numeric_type(
&self,
field_type: &Type,
) -> bool {
let type_name = utils::extract_type_name(field_type);
matches!(
type_name.as_str(),
"i8" | "i16"
| "i32"
| "i64"
| "i128"
| "isize"
| "u8"
| "u16"
| "u32"
| "u64"
| "u128"
| "usize"
| "f32"
| "f64"
)
}
}
impl Default for NumericConverter {
fn default() -> Self {
Self::new()
}
}
impl TypeConverter for NumericConverter {
fn convert_field_to_json_value(
&self,
field: &Field,
) -> MacroResult<TokenStream2> {
if !self.supports_type(&field.ty) {
let type_name = utils::extract_type_name(&field.ty);
let field_name = field
.ident
.as_ref()
.map(|i| i.to_string())
.unwrap_or_else(|| "匿名字段".to_string());
return Err(MacroError::unsupported_field_type(
&field_name,
&type_name,
field,
));
}
let field_name = field.ident.as_ref().ok_or_else(|| {
MacroError::parse_error("字段缺少名称(不支持匿名字段)", field)
})?;
let conversion_code =
utils::generate_field_conversion(field_name, &field.ty);
Ok(conversion_code)
}
fn supports_type(
&self,
field_type: &Type,
) -> bool {
self.is_numeric_type(field_type)
}
fn priority(&self) -> i32 {
95 }
fn name(&self) -> &'static str {
"NumericConverter"
}
}
#[derive(Debug, Clone)]
pub struct BooleanConverter;
impl BooleanConverter {
pub fn new() -> Self {
Self
}
fn is_boolean_type(
&self,
field_type: &Type,
) -> bool {
if utils::is_option_type(field_type) {
if let Some(inner_type) =
utils::extract_option_inner_type(field_type)
{
self.is_basic_boolean_type(inner_type)
} else {
false
}
} else {
self.is_basic_boolean_type(field_type)
}
}
fn is_basic_boolean_type(
&self,
field_type: &Type,
) -> bool {
let type_name = utils::extract_type_name(field_type);
type_name == "bool"
}
}
impl Default for BooleanConverter {
fn default() -> Self {
Self::new()
}
}
impl TypeConverter for BooleanConverter {
fn convert_field_to_json_value(
&self,
field: &Field,
) -> MacroResult<TokenStream2> {
if !self.supports_type(&field.ty) {
let type_name = utils::extract_type_name(&field.ty);
let field_name = field
.ident
.as_ref()
.map(|i| i.to_string())
.unwrap_or_else(|| "匿名字段".to_string());
return Err(MacroError::unsupported_field_type(
&field_name,
&type_name,
field,
));
}
let field_name = field.ident.as_ref().ok_or_else(|| {
MacroError::parse_error("字段缺少名称(不支持匿名字段)", field)
})?;
let conversion_code =
utils::generate_field_conversion(field_name, &field.ty);
Ok(conversion_code)
}
fn supports_type(
&self,
field_type: &Type,
) -> bool {
self.is_boolean_type(field_type)
}
fn priority(&self) -> i32 {
85 }
fn name(&self) -> &'static str {
"BooleanConverter"
}
}
#[derive(Debug, Clone)]
pub struct SpecialTypeConverter;
impl SpecialTypeConverter {
pub fn new() -> Self {
Self
}
#[allow(unused_variables)]
fn is_special_type(
&self,
_field_type: &Type,
) -> bool {
false
}
}
impl Default for SpecialTypeConverter {
fn default() -> Self {
Self::new()
}
}
impl TypeConverter for SpecialTypeConverter {
fn convert_field_to_json_value(
&self,
field: &Field,
) -> MacroResult<TokenStream2> {
let type_name = utils::extract_type_name(&field.ty);
let field_name = field
.ident
.as_ref()
.map(|i| i.to_string())
.unwrap_or_else(|| "匿名字段".to_string());
Err(MacroError::unsupported_field_type(&field_name, &type_name, field))
}
fn supports_type(
&self,
field_type: &Type,
) -> bool {
self.is_special_type(field_type)
}
fn priority(&self) -> i32 {
50 }
fn name(&self) -> &'static str {
"SpecialTypeConverter"
}
}
pub fn get_all_builtin_converters() -> Vec<Box<dyn TypeConverter>> {
let mut converters: Vec<Box<dyn TypeConverter>> = vec![
Box::new(NumericConverter::new()),
Box::new(StringConverter::new()),
Box::new(BooleanConverter::new()),
Box::new(SpecialTypeConverter::new()),
];
converters.sort_by_key(|b| std::cmp::Reverse(b.priority()));
converters
}
#[cfg(test)]
mod tests {
use super::*;
use syn::parse_quote;
#[test]
fn test_string_converter() {
let converter = StringConverter::new();
let string_type: Type = parse_quote! { String };
assert!(converter.supports_type(&string_type));
let str_type: Type = parse_quote! { &str };
assert!(converter.supports_type(&str_type));
let option_string: Type = parse_quote! { Option<String> };
assert!(converter.supports_type(&option_string));
let i32_type: Type = parse_quote! { i32 };
assert!(!converter.supports_type(&i32_type));
assert_eq!(converter.name(), "StringConverter");
assert_eq!(converter.priority(), 90);
}
#[test]
fn test_numeric_converter() {
let converter = NumericConverter::new();
let i32_type: Type = parse_quote! { i32 };
assert!(converter.supports_type(&i32_type));
let f64_type: Type = parse_quote! { f64 };
assert!(converter.supports_type(&f64_type));
let option_i32: Type = parse_quote! { Option<i32> };
assert!(converter.supports_type(&option_i32));
let string_type: Type = parse_quote! { String };
assert!(!converter.supports_type(&string_type));
assert_eq!(converter.name(), "NumericConverter");
assert_eq!(converter.priority(), 95);
}
#[test]
fn test_boolean_converter() {
let converter = BooleanConverter::new();
let bool_type: Type = parse_quote! { bool };
assert!(converter.supports_type(&bool_type));
let option_bool: Type = parse_quote! { Option<bool> };
assert!(converter.supports_type(&option_bool));
let i32_type: Type = parse_quote! { i32 };
assert!(!converter.supports_type(&i32_type));
assert_eq!(converter.name(), "BooleanConverter");
assert_eq!(converter.priority(), 85);
}
#[test]
fn test_special_type_converter() {
let converter = SpecialTypeConverter::new();
let vec_type: Type = parse_quote! { Vec<String> };
assert!(!converter.supports_type(&vec_type));
let hashmap_type: Type = parse_quote! { HashMap<String, i32> };
assert!(!converter.supports_type(&hashmap_type));
assert_eq!(converter.name(), "SpecialTypeConverter");
assert_eq!(converter.priority(), 50);
}
#[test]
fn test_string_converter_code_generation() {
let converter = StringConverter::new();
let field: syn::Field = parse_quote! {
name: String
};
let result = converter.convert_field_to_json_value(&field);
assert!(result.is_ok());
let code = result.unwrap();
let code_str = code.to_string();
assert!(code_str.contains("serde_json::to_value"));
assert!(code_str.contains("name"));
}
#[test]
fn test_numeric_converter_code_generation() {
let converter = NumericConverter::new();
let field: syn::Field = parse_quote! {
age: i32
};
let result = converter.convert_field_to_json_value(&field);
assert!(result.is_ok());
let code = result.unwrap();
let code_str = code.to_string();
assert!(code_str.contains("serde_json::to_value"));
assert!(code_str.contains("age"));
}
#[test]
fn test_boolean_converter_code_generation() {
let converter = BooleanConverter::new();
let field: syn::Field = parse_quote! {
active: bool
};
let result = converter.convert_field_to_json_value(&field);
assert!(result.is_ok());
let code = result.unwrap();
let code_str = code.to_string();
assert!(code_str.contains("serde_json::to_value"));
assert!(code_str.contains("active"));
}
#[test]
fn test_special_converter_error_handling() {
let converter = SpecialTypeConverter::new();
let field: syn::Field = parse_quote! {
data: Vec<String>
};
let result = converter.convert_field_to_json_value(&field);
assert!(result.is_err());
if let Err(MacroError::UnsupportedFieldType {
field_name,
field_type,
..
}) = result
{
assert_eq!(field_name, "data");
assert!(field_type.contains("Vec"));
} else {
panic!("期望 UnsupportedFieldType 错误");
}
}
#[test]
fn test_get_all_builtin_converters() {
let converters = get_all_builtin_converters();
assert_eq!(converters.len(), 4);
let names: Vec<_> = converters.iter().map(|c| c.name()).collect();
assert!(names.contains(&"NumericConverter"));
assert!(names.contains(&"StringConverter"));
assert!(names.contains(&"BooleanConverter"));
assert!(names.contains(&"SpecialTypeConverter"));
for i in 0..converters.len() - 1 {
assert!(
converters[i].priority() >= converters[i + 1].priority(),
"转换器未按优先级正确排序"
);
}
}
#[test]
fn test_converter_defaults() {
let string_converter = StringConverter;
assert_eq!(string_converter.name(), "StringConverter");
let numeric_converter = NumericConverter;
assert_eq!(numeric_converter.name(), "NumericConverter");
let boolean_converter = BooleanConverter;
assert_eq!(boolean_converter.name(), "BooleanConverter");
let special_converter = SpecialTypeConverter;
assert_eq!(special_converter.name(), "SpecialTypeConverter");
}
#[test]
fn test_converter_type_coverage() {
let converters = get_all_builtin_converters();
let test_types = vec![
(parse_quote! { String }, true),
(parse_quote! { i32 }, true),
(parse_quote! { f64 }, true),
(parse_quote! { bool }, true),
(parse_quote! { Option<String> }, true),
(parse_quote! { Option<i32> }, true),
(parse_quote! { Option<bool> }, true),
(parse_quote! { Vec<String> }, false),
(parse_quote! { HashMap<String, i32> }, false),
];
for (ty, should_be_supported) in test_types {
let supported = converters.iter().any(|c| c.supports_type(&ty));
assert_eq!(
supported, should_be_supported,
"类型 {ty:?} 的支持状态不正确"
);
}
}
}