use proc_macro2::TokenStream as TokenStream2;
use syn::DeriveInput;
use crate::common::{MacroResult, MacroError};
use crate::parser::{AttributeParser, Validator};
use crate::generator::{GeneratorFactory, CodeGenerator};
pub fn process_derive_mark(input: DeriveInput) -> MacroResult<TokenStream2> {
let config =
AttributeParser::parse_mark_attributes(&input).map_err(|e| {
MacroError::parse_error(&format!("Mark 属性解析失败: {e}"), &input)
})?;
Validator::validate_mark_config(&config).map_err(|e| {
MacroError::validation_error(&format!("Mark 配置验证失败: {e}"), &input)
})?;
let generator = GeneratorFactory::create_mark_generator(&input, &config);
let generated_code = generator.generate().map_err(|e| {
MacroError::generation_error(&format!("Mark 代码生成失败: {e}"), &input)
})?;
Ok(generated_code)
}
pub fn process_derive_mark_with_recovery(input: DeriveInput) -> TokenStream2 {
match process_derive_mark(input) {
Ok(tokens) => tokens,
Err(error) => {
let error_message = create_friendly_error_message(&error);
quote::quote! {
compile_error!(#error_message);
}
},
}
}
fn create_friendly_error_message(error: &MacroError) -> String {
match error {
MacroError::ParseError { message, .. } => {
format!(
"ModuForge Mark 派生宏解析错误:\n\n{message}\n\n帮助信息:\n• 检查宏属性的语法是否正确\n• 确保所有必需的属性都已设置\n• 参考文档中的示例用法"
)
},
MacroError::ValidationError { message, .. } => {
format!(
"ModuForge Mark 派生宏验证错误:\n\n{message}\n\n帮助信息:\n• 检查字段类型是否受支持\n• 确保属性值符合要求\n• 验证配置的一致性"
)
},
MacroError::UnsupportedFieldType { field_name, field_type, .. } => {
format!(
"ModuForge Mark 派生宏类型错误:\n\n字段 '{field_name}' 的类型 '{field_type}' 不受支持\n\n支持的类型包括:\n• 基本类型: String, i32, f64, bool 等\n• 可选类型: Option<T> (T 为任意支持的基本类型)\n\n如需支持其他类型,请参考自定义转换器文档"
)
},
MacroError::GenerationError { message, .. } => {
format!(
"ModuForge Mark 派生宏代码生成错误:\n\n{message}\n\n这通常是内部错误,请报告此问题:\n• 包含完整的错误信息\n• 提供导致错误的代码示例\n• 说明您的使用场景"
)
},
MacroError::MissingAttribute { attribute, .. } => {
format!(
"ModuForge Mark 派生宏缺少属性错误:\n\n缺少必需的属性: {attribute}\n\n帮助信息:\n• 确保在结构体上添加了所有必需的宏属性\n• 检查属性名称的拼写是否正确\n• 参考文档中的完整示例"
)
},
MacroError::InvalidAttributeValue {
attribute, value, reason, ..
} => {
format!(
"ModuForge Mark 派生宏无效属性值错误:\n\n属性 '{attribute}' 的值 '{value}' 无效: {reason}\n\n帮助信息:\n• 检查属性值的格式是否符合要求\n• 确认属性值不为空且符合语法规则\n• 参考文档中的有效属性值示例"
)
},
MacroError::SyntaxError(syn_error) => {
format!(
"ModuForge Mark 派生宏语法错误:\n\n{syn_error}\n\n帮助信息:\n• 检查代码的语法是否正确\n• 确认所有括号和引号都已正确闭合\n• 验证结构体定义的完整性"
)
},
}
}
#[cfg(test)]
mod tests {
use super::*;
use syn::parse_quote;
#[test]
fn test_basic_mark_derive_processing() {
let input: DeriveInput = parse_quote! {
#[derive(Mark)]
#[mark_type = "bold"]
struct TestMark {
#[attr]
weight: String,
}
};
let result = process_derive_mark(input);
assert!(result.is_ok());
let code = result.unwrap();
let code_str = code.to_string();
assert!(code_str.contains("impl TestMark"));
assert!(code_str.contains("pub fn to_mark"));
assert!(code_str.contains("mf_model::mark::Mark"));
assert!(code_str.contains("bold"));
}
#[test]
fn test_full_mark_derive_processing() {
let input: DeriveInput = parse_quote! {
#[derive(Mark)]
#[mark_type = "styled"]
struct TestMark {
#[attr]
weight: String,
#[attr]
color: Option<String>,
}
};
let result = process_derive_mark(input);
assert!(result.is_ok());
let code = result.unwrap();
let code_str = code.to_string();
assert!(code_str.contains("styled"));
assert!(code_str.contains("weight"));
assert!(code_str.contains("color"));
}
#[test]
fn test_missing_required_attribute_error() {
let input: DeriveInput = parse_quote! {
#[derive(Mark)]
struct TestMark {
#[attr]
weight: String,
}
};
let result = process_derive_mark(input);
assert!(result.is_err());
if let Err(MacroError::ParseError { message, .. }) = result {
assert!(message.contains("mark_type") || message.contains("必需"));
} else {
panic!("期望 ParseError 或 ValidationError");
}
}
#[test]
fn test_unsupported_field_type_error() {
let input: DeriveInput = parse_quote! {
#[derive(Mark)]
#[mark_type = "test"]
struct TestMark {
#[attr]
data: Vec<String>, }
};
let result = process_derive_mark(input);
assert!(result.is_err());
match result.unwrap_err() {
MacroError::UnsupportedFieldType {
field_name, field_type, ..
} => {
assert_eq!(field_name, "data");
assert!(field_type.contains("Vec"));
},
MacroError::ValidationError { .. } => {
},
_ => panic!("期望 UnsupportedFieldType 或 ValidationError"),
}
}
#[test]
fn test_error_recovery() {
let input: DeriveInput = parse_quote! {
#[derive(Mark)]
struct TestMark {
invalid_field: UnknownType,
}
};
let result = process_derive_mark_with_recovery(input);
let result_str = result.to_string();
assert!(result_str.contains("compile_error"));
}
#[test]
fn test_friendly_error_message_creation() {
let parse_error = MacroError::ParseError {
message: "测试解析错误".to_string(),
span: None,
};
let friendly_message = create_friendly_error_message(&parse_error);
assert!(friendly_message.contains("ModuForge Mark 派生宏解析错误"));
assert!(friendly_message.contains("帮助信息"));
assert!(friendly_message.contains("测试解析错误"));
let unsupported_error = MacroError::UnsupportedFieldType {
field_name: "test_field".to_string(),
field_type: "Vec<String>".to_string(),
span: None,
};
let friendly_message =
create_friendly_error_message(&unsupported_error);
assert!(friendly_message.contains("类型错误"));
assert!(friendly_message.contains("test_field"));
assert!(friendly_message.contains("Vec<String>"));
assert!(friendly_message.contains("支持的类型"));
}
#[test]
fn test_mark_without_attr_fields() {
let input: DeriveInput = parse_quote! {
#[derive(Mark)]
#[mark_type = "simple"]
struct SimpleMark;
};
let result = process_derive_mark(input);
assert!(result.is_ok());
let code = result.unwrap();
let code_str = code.to_string();
assert!(code_str.contains("impl SimpleMark"));
assert!(code_str.contains("simple"));
assert!(
code_str.contains("imbl")
&& code_str.contains("HashMap")
&& code_str.contains("new")
);
}
#[test]
fn test_complex_scenario_end_to_end() {
let input: DeriveInput = parse_quote! {
#[derive(Mark)]
#[mark_type = "complex_style"]
struct ComplexStyleMark {
#[attr]
weight: String,
#[attr]
color: Option<String>,
#[attr]
size: Option<f64>,
#[attr]
opacity: Option<f32>,
#[attr]
is_underlined: Option<bool>,
internal_id: uuid::Uuid,
cached_data: Vec<u8>,
}
};
let result = process_derive_mark(input);
assert!(result.is_ok());
let code = result.unwrap();
let code_str = code.to_string();
assert!(code_str.contains("complex_style"));
assert!(code_str.contains("weight"));
assert!(code_str.contains("color"));
assert!(code_str.contains("size"));
assert!(code_str.contains("opacity"));
assert!(code_str.contains("is_underlined"));
assert!(!code_str.contains("internal_id"));
assert!(!code_str.contains("cached_data"));
}
}