use proc_macro2::TokenStream;
use quote::quote;
use syn::spanned::Spanned;
pub type ComponentResult<T> = Result<T, ComponentError>;
#[derive(Debug)]
pub enum ComponentError {
InvalidSignature {
message: String,
span: proc_macro2::Span,
suggestion: Option<String>,
},
InvalidReturnType {
message: String,
span: proc_macro2::Span,
expected: String,
},
InvalidParameters {
message: String,
span: proc_macro2::Span,
suggestion: Option<String>,
},
UnsupportedGenerics {
message: String,
span: proc_macro2::Span,
limitation: String,
},
InvalidSyntax(String),
InternalError { message: String, context: String },
}
impl ComponentError {
pub fn invalid_signature<T: Spanned>(
item: &T,
message: impl Into<String>,
suggestion: Option<impl Into<String>>,
) -> Self {
Self::InvalidSignature {
message: message.into(),
span: item.span(),
suggestion: suggestion.map(|s| s.into()),
}
}
pub fn invalid_return_type<T: Spanned>(
item: &T,
message: impl Into<String>,
expected: impl Into<String>,
) -> Self {
Self::InvalidReturnType {
message: message.into(),
span: item.span(),
expected: expected.into(),
}
}
pub fn invalid_parameters<T: Spanned>(
item: &T,
message: impl Into<String>,
suggestion: Option<impl Into<String>>,
) -> Self {
Self::InvalidParameters {
message: message.into(),
span: item.span(),
suggestion: suggestion.map(|s| s.into()),
}
}
pub fn unsupported_generics<T: Spanned>(
item: &T,
message: impl Into<String>,
limitation: impl Into<String>,
) -> Self {
Self::UnsupportedGenerics {
message: message.into(),
span: item.span(),
limitation: limitation.into(),
}
}
pub fn internal_error(message: impl Into<String>, context: impl Into<String>) -> Self {
Self::InternalError {
message: message.into(),
context: context.into(),
}
}
pub fn to_compile_error(&self) -> TokenStream {
match self {
ComponentError::InvalidSignature {
message,
span,
suggestion,
} => {
let error_msg = if let Some(suggestion) = suggestion {
format!("{}\n\nSuggestion: {}", message, suggestion)
} else {
message.clone()
};
quote::quote_spanned! { *span =>
compile_error!(#error_msg);
}
}
ComponentError::InvalidReturnType {
message,
span,
expected,
} => {
let error_msg = format!("{}\n\nExpected: {}", message, expected);
quote::quote_spanned! { *span =>
compile_error!(#error_msg);
}
}
ComponentError::InvalidParameters {
message,
span,
suggestion,
} => {
let error_msg = if let Some(suggestion) = suggestion {
format!("{}\n\nSuggestion: {}", message, suggestion)
} else {
message.clone()
};
quote::quote_spanned! { *span =>
compile_error!(#error_msg);
}
}
ComponentError::UnsupportedGenerics {
message,
span,
limitation,
} => {
let error_msg = format!("{}\n\nLimitation: {}", message, limitation);
quote::quote_spanned! { *span =>
compile_error!(#error_msg);
}
}
ComponentError::InvalidSyntax(message) => {
quote! {
compile_error!(#message);
}
}
ComponentError::InternalError { message, context } => {
let error_msg = format!(
"Internal component macro error: {}\nContext: {}\n\nPlease report this as a bug.",
message, context
);
quote! {
compile_error!(#error_msg);
}
}
}
}
}
impl std::fmt::Display for ComponentError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ComponentError::InvalidSignature { message, .. } => {
write!(f, "Invalid component signature: {}", message)
}
ComponentError::InvalidReturnType {
message, expected, ..
} => {
write!(
f,
"Invalid return type: {}. Expected: {}",
message, expected
)
}
ComponentError::InvalidParameters { message, .. } => {
write!(f, "Invalid parameters: {}", message)
}
ComponentError::UnsupportedGenerics {
message,
limitation,
..
} => {
write!(
f,
"Unsupported generics: {}. Limitation: {}",
message, limitation
)
}
ComponentError::InvalidSyntax(message) => {
write!(f, "Invalid syntax: {}", message)
}
ComponentError::InternalError { message, context } => {
write!(f, "Internal error: {} (Context: {})", message, context)
}
}
}
}
impl std::error::Error for ComponentError {}
pub trait IntoComponentError<T> {
fn into_component_error(self) -> ComponentResult<T>;
}
impl<T> IntoComponentError<T> for syn::Result<T> {
fn into_component_error(self) -> ComponentResult<T> {
self.map_err(|syn_error| {
ComponentError::internal_error(syn_error.to_string(), "Failed to parse syntax tree")
})
}
}
pub mod messages {
pub mod signature {
pub const MISSING_RETURN_TYPE: &str =
"Component function must have an explicit return type";
pub const INVALID_RETURN_TYPE: &str =
"Component function must return Element, VNode, or a compatible type";
pub const ASYNC_NOT_SUPPORTED: &str =
"Async component functions are not currently supported";
pub const UNSAFE_NOT_SUPPORTED: &str = "Unsafe component functions are not supported";
}
pub mod parameters {
pub const INVALID_PATTERN: &str =
"Component parameters must be simple identifiers (e.g., `name: String`)";
pub const SELF_PARAMETER: &str = "Component functions cannot have `self` parameters";
pub const REFERENCE_PARAMETER_SUGGESTION: &str =
"Use `props: &PropsStruct` for props-based components";
pub const DIRECT_PARAMETER_SUGGESTION: &str =
"Use direct parameters like `name: String, age: u32` for direct parameter components";
}
pub mod generics {
pub const COMPLEX_BOUNDS: &str =
"Complex generic bounds are not fully supported in component functions";
pub const LIFETIME_PARAMETERS: &str =
"Lifetime parameters in component functions have limited support";
}
}