use std::fmt::Display;
use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
pub struct Error {
pub start: Span,
pub end: Span,
pub kind: ErrorKind,
}
pub enum ErrorKind {
UnknownObjcBinding,
BadBindingBrackets,
MethodBeforeClass,
UnnamedClass,
NoSemicolonAfterClass,
ClassDefinedTwice(String),
NoType,
BorrowsUnsupported,
Method(MethodError),
Attribute(AttributeError),
GiveUp,
NoComma,
}
impl Display for ErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let err = match self {
Self::UnknownObjcBinding => {
"Unknown Objective-C binding. Objective-rust only supports `extern \"objc\"` blocks.".into()
}
Self::BadBindingBrackets => "`extern \"objc\"` blocks must use `{}`.".into(),
Self::MethodBeforeClass => "A class needs to be defined before methods can be defined.".into(),
Self::UnnamedClass => "Expected a class name after `type`.".into(),
Self::NoSemicolonAfterClass => "Expected a `;` beside the class name.".into(),
Self::ClassDefinedTwice(name) => format!("Class {name} is defined multiple times."),
Self::NoType => "Expected a type here.".into(),
Self::BorrowsUnsupported => "Borrows are currently unsupported in Objective-Rust for safety reasons.".into(),
Self::Method(method) => method.to_string(),
Self::Attribute(err) => err.to_string(),
Self::GiveUp => "Unknown syntax".into(),
Self::NoComma => "Expected a comma between types".into(),
};
write!(f, "{err}")
}
}
pub enum MethodError {
NoName,
NoArgs,
NoReturnTypeOrSemicolon,
NoSemicolon,
NoArgumentName,
NoArgumentColon,
NoArgumentComma,
ExpectedSelfReference,
}
impl Display for MethodError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let err = match self {
Self::NoName => "Expected a method name after `fn`.",
Self::NoArgs => "Expected method arguments after the method name.",
Self::NoReturnTypeOrSemicolon => {
"Expected a return type or `;` after the method arguments."
}
Self::NoSemicolon => "Expected a `;` after the method return type.",
Self::NoArgumentName => "Expected an argument name.",
Self::NoArgumentColon => "Expected a `:` after the argument's name.",
Self::NoArgumentComma => "Expected a `,` in between arguments.",
Self::ExpectedSelfReference => "Expected `self` or `mut self` after the `&`.",
};
write!(f, "{err}")
}
}
pub enum AttributeError {
NoBrackets,
NoName,
Unknown,
NoEquals,
NoValue,
Type(String),
}
impl Display for AttributeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let err = match self {
Self::NoBrackets => "Expected brackets afer `#` in attribute.".into(),
Self::NoName => "Expected an attribute name after `[`.".into(),
Self::Unknown => "Unknown attribute.".into(),
Self::NoEquals => "Expected `=` after the attribute name.".into(),
Self::NoValue => "Expected a value after the `=`.".into(),
Self::Type(expected) => format!("Expected a `{expected}` literal."),
};
write!(f, "{err}")
}
}
impl From<Error> for TokenStream {
fn from(value: Error) -> Self {
TokenStream::from_iter(vec![
TokenTree::Punct({
let mut punct = Punct::new(':', Spacing::Joint);
punct.set_span(value.start);
punct
}),
TokenTree::Punct({
let mut punct = Punct::new(':', Spacing::Alone);
punct.set_span(value.start);
punct
}),
TokenTree::Ident(Ident::new("core", value.start)),
TokenTree::Punct(Punct::new(':', Spacing::Joint)),
TokenTree::Punct(Punct::new(':', Spacing::Alone)),
TokenTree::Ident(Ident::new("compile_error", value.start)),
TokenTree::Punct(Punct::new('!', Spacing::Alone)),
TokenTree::Group({
let mut group = Group::new(
Delimiter::Brace,
TokenStream::from_iter(vec![TokenTree::Literal(Literal::string(
&value.kind.to_string(),
))]),
);
group.set_span(value.end);
group
}),
])
}
}