use {
crate::error::{
Error,
ErrorAccumulator,
},
proc_macro2::TokenStream,
quote::{
ToTokens,
spanned::Spanned as quote_spanned,
},
syn::{
Item,
Type,
spanned::Spanned,
},
};
#[derive(Debug, Clone)]
pub struct Ast {
pub name: String,
pub fields: Vec<Field>,
}
#[derive(Debug, Clone)]
pub struct Field {
pub(crate) name: String,
pub(crate) ty: FieldType,
pub(crate) is_option: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum FieldType {
Unknown(String),
Literal(&'static str),
NodeId,
Span,
NodeList,
ControlSpan,
KeywordSpan,
ScalarSpan,
LiteralSpan,
TriviaSpan,
StringSpan,
Ident,
}
fn ident_to_type(s: &str) -> FieldType {
match s {
| "CstNodeId" => FieldType::NodeId,
| "Span" => FieldType::Span,
| "NodeList" => FieldType::NodeList,
| "ControlSpan" => FieldType::ControlSpan,
| "KeywordSpan" => FieldType::KeywordSpan,
| "ScalarSpan" => FieldType::ScalarSpan,
| "LiteralSpan" => FieldType::LiteralSpan,
| "TriviaSpan" => FieldType::TriviaSpan,
| "String" => FieldType::StringSpan,
| "Ident" => FieldType::Ident,
| "bool" => FieldType::Literal("bool"),
| "usize" => FieldType::Literal("usize"),
| "u8" => FieldType::Literal("u8"),
| "u16" => FieldType::Literal("u16"),
| "u32" => FieldType::Literal("u32"),
| "u64" => FieldType::Literal("u64"),
| "u128" => FieldType::Literal("u128"),
| "isize" => FieldType::Literal("isize"),
| "i8" => FieldType::Literal("i8"),
| "i16" => FieldType::Literal("i16"),
| "i32" => FieldType::Literal("i32"),
| "i64" => FieldType::Literal("i64"),
| "i128" => FieldType::Literal("i128"),
| "f32" => FieldType::Literal("f32"),
| "f64" => FieldType::Literal("f64"),
| _ => FieldType::Unknown(s.to_owned()),
}
}
pub fn parse(ts: TokenStream) -> Result<Ast, syn::Error> {
let item = ts;
let span = item.__span();
match syn::parse2::<Item>(item) {
| Ok(Item::Struct(item_stct)) => {
let name = item_stct.ident.clone().to_string();
let fields = {
match item_stct.fields {
| syn::Fields::Named(fields_named) => {
parse_fields_with_accumulation(&fields_named.named)?
},
| _ => unimplemented!(),
}
};
Ok(Ast { name, fields })
},
| Ok(_) => {
let error_details = Error::ParseItemNotStruct(span).get();
let mut syn_error =
syn::Error::new(error_details.span, &error_details.message);
if let Some(help) = &error_details.help {
let help_error =
syn::Error::new(error_details.span, format!("help: {help}"));
syn_error.combine(help_error);
}
if let Some(hints) = &error_details.hints {
let hints_error =
syn::Error::new(error_details.span, format!("hint: {hints}"));
syn_error.combine(hints_error);
}
Err(syn_error)
},
| Err(parse_error) => {
let error_details = Error::Parser(span).get();
let mut combined_error =
syn::Error::new(error_details.span, &error_details.message);
combined_error.combine(parse_error);
Err(combined_error)
},
}
}
fn parse_fields_with_accumulation(
fields: &syn::punctuated::Punctuated<syn::Field, syn::Token![,]>,
) -> Result<Vec<Field>, syn::Error> {
let mut accumulator = ErrorAccumulator::new();
let mut parsed_fields = Vec::new();
for field in fields {
match parse_field(field) {
| Ok(parsed_field) => {
parsed_fields.push(parsed_field);
},
| Err(error) => {
accumulator.add_error(error);
},
}
}
accumulator.into_result(parsed_fields)
}
fn parse_field(f: &syn::Field) -> Result<Field, Error> {
let name = match &f.ident {
| Some(ident) => ident.to_string(),
| None => {
return Err(Error::MissingFieldIdentifier(f.span()));
},
};
let (is_option, found_type) = {
match &f.ty {
| Type::Path(type_path) => {
let mut segments = type_path.path.segments.clone().into_iter();
if let Some(first) = segments.next() {
let s = first.ident.clone().to_string();
if &s == "Option" {
let tty = match first.arguments {
| syn::PathArguments::AngleBracketed(ref args) => {
match args.args.first() {
| Some(syn::GenericArgument::Type(ty)) => {
match ty {
| Type::Path(type_path) => {
ident_to_type(&type_path.into_token_stream().to_string())
},
| _ => {
return Err(Error::UnsupportedFieldType(
f.ty.span(),
"expected angle-bracketed arguments".to_string(),
));
},
}
},
| _ => {
return Err(Error::UnsupportedFieldType(
f.ty.span(),
"expected type argument".to_string(),
));
},
}
},
| _ => {
return Err(Error::UnsupportedFieldType(
f.ty.span(),
"expected type argument".to_string(),
));
},
};
(true, tty)
} else {
(
false,
ident_to_type(&type_path.into_token_stream().to_string()),
)
}
} else {
return Err(Error::EmptyTypePath(type_path.span()));
}
},
| _ => {
return Err(Error::UnsupportedFieldType(
f.ty.span(),
format!("{:?}", f.ty),
));
},
}
};
Ok(Field {
name,
ty: found_type,
is_option,
})
}