use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::quote;
use syn::spanned::Spanned;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum MacroError {
#[error("缺少必需的宏属性: {attribute}")]
MissingAttribute {
attribute: String,
span: Option<Span>,
},
#[error("无效的属性值 '{value}' 用于属性 '{attribute}': {reason}")]
InvalidAttributeValue {
attribute: String,
value: String,
reason: String,
span: Option<Span>,
},
#[error("不支持的字段类型 '{field_type}' 在字段 '{field_name}' 中")]
UnsupportedFieldType {
field_name: String,
field_type: String,
span: Option<Span>,
},
#[error("属性解析错误: {message}")]
ParseError {
message: String,
span: Option<Span>,
},
#[error("代码生成错误: {message}")]
GenerationError {
message: String,
span: Option<Span>,
},
#[error("验证错误: {message}")]
ValidationError {
message: String,
span: Option<Span>,
},
#[error("语法错误: {0}")]
SyntaxError(#[from] syn::Error),
}
impl MacroError {
pub fn to_compile_error(&self) -> TokenStream2 {
let message = self.to_string();
let span = self.get_span().unwrap_or_else(Span::call_site);
quote::quote_spanned! { span =>
compile_error!(#message);
}
}
pub fn missing_attribute<T: Spanned>(
attribute: &str,
spanned: &T,
) -> Self {
Self::MissingAttribute {
attribute: attribute.to_string(),
span: Some(spanned.span()),
}
}
pub fn invalid_attribute_value<T: Spanned>(
attribute: &str,
value: &str,
reason: &str,
spanned: &T,
) -> Self {
Self::InvalidAttributeValue {
attribute: attribute.to_string(),
value: value.to_string(),
reason: reason.to_string(),
span: Some(spanned.span()),
}
}
pub fn unsupported_field_type<T: Spanned>(
field_name: &str,
field_type: &str,
spanned: &T,
) -> Self {
Self::UnsupportedFieldType {
field_name: field_name.to_string(),
field_type: field_type.to_string(),
span: Some(spanned.span()),
}
}
pub fn parse_error<T: Spanned>(
message: &str,
spanned: &T,
) -> Self {
Self::ParseError {
message: message.to_string(),
span: Some(spanned.span()),
}
}
pub fn validation_error<T: Spanned>(
message: &str,
spanned: &T,
) -> Self {
Self::ValidationError {
message: message.to_string(),
span: Some(spanned.span()),
}
}
pub fn generation_error<T: Spanned>(
message: &str,
spanned: &T,
) -> Self {
Self::GenerationError {
message: message.to_string(),
span: Some(spanned.span()),
}
}
fn get_span(&self) -> Option<Span> {
match self {
Self::MissingAttribute { span, .. } => *span,
Self::InvalidAttributeValue { span, .. } => *span,
Self::UnsupportedFieldType { span, .. } => *span,
Self::ParseError { span, .. } => *span,
Self::GenerationError { span, .. } => *span,
Self::ValidationError { span, .. } => *span,
Self::SyntaxError(err) => Some(err.span()),
}
}
pub fn suggestion(&self) -> String {
match self {
Self::MissingAttribute { attribute, .. } => {
match attribute.as_str() {
"node_type" => "请在结构体上添加 #[node_type = \"类型名\"] 属性,例如: #[node_type = \"paragraph\"]".to_string(),
"mark_type" => "请在结构体上添加 #[mark_type = \"类型名\"] 属性,例如: #[mark_type = \"bold\"]".to_string(),
_ => format!(
"请在结构体上添加 #[{attribute} = \"值\"] 属性"
),
}
},
Self::InvalidAttributeValue { attribute, .. } => {
format!("请检查 #{attribute} 属性的值格式是否正确")
},
Self::UnsupportedFieldType { field_name, field_type, .. } => {
format!(
"字段 '{field_name}' 的类型 '{field_type}' 不受支持,请使用支持的基本类型:String, i32, i64, f32, f64, bool 或其 Option 包装版本"
)
},
_ => "请检查宏的使用方式是否符合文档要求".to_string(),
}
}
}
pub type MacroResult<T> = Result<T, MacroError>;
pub fn create_compile_error(message: &str) -> TokenStream2 {
quote! {
compile_error!(#message);
}
}
pub fn create_compile_error_with_suggestion(
error_msg: &str,
suggestion: &str,
) -> TokenStream2 {
let combined_message = format!("{error_msg}\n\n修复建议: {suggestion}");
quote! {
compile_error!(#combined_message);
}
}
#[cfg(test)]
mod tests {
use super::*;
use quote::quote;
#[test]
fn test_missing_attribute_error() {
let tokens = quote! { struct Test {} };
let error = MacroError::missing_attribute("node_type", &tokens);
let error_str = error.to_string();
assert!(error_str.contains("缺少必需的宏属性: node_type"));
let suggestion = error.suggestion();
assert!(suggestion.contains("node_type"));
assert!(suggestion.contains("paragraph"));
}
#[test]
fn test_invalid_attribute_value_error() {
let tokens = quote! { #[node_type = ""] };
let error = MacroError::invalid_attribute_value(
"node_type",
"",
"值不能为空",
&tokens,
);
let error_str = error.to_string();
assert!(error_str.contains("无效的属性值"));
assert!(error_str.contains("node_type"));
assert!(error_str.contains("值不能为空"));
}
#[test]
fn test_to_compile_error() {
let error = MacroError::ParseError {
message: "测试错误".to_string(),
span: None,
};
let compile_error = error.to_compile_error();
let compile_error_str = compile_error.to_string();
assert!(compile_error_str.contains("compile_error"));
assert!(compile_error_str.contains("测试错误"));
}
#[test]
fn test_error_suggestions() {
let missing_node_type = MacroError::MissingAttribute {
attribute: "node_type".to_string(),
span: None,
};
let suggestion = missing_node_type.suggestion();
assert!(suggestion.contains("node_type"));
assert!(suggestion.contains("paragraph"));
let missing_mark_type = MacroError::MissingAttribute {
attribute: "mark_type".to_string(),
span: None,
};
let suggestion = missing_mark_type.suggestion();
assert!(suggestion.contains("mark_type"));
assert!(suggestion.contains("bold"));
}
}