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_vec: bool,
pub(crate) is_optional: bool,
pub(crate) is_field: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum FieldType {
Unknown,
#[allow(dead_code)]
Error(String),
Path(String),
Enum(String),
SingleNode(String),
MultipleNode(Vec<String>),
EnumNodeId(String),
Literal(&'static str),
String,
Span,
Ident,
SpannedIdent,
}
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().trim().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()));
},
};
match &f.ty {
| Type::Path(type_path) => {
let mut segments = type_path.path.segments.clone().into_iter();
let Some(seg) = segments.next() else {
return Err(Error::EmptyTypePath(type_path.span()));
};
let fld = Field {
name,
ty: FieldType::Unknown,
is_vec: false,
is_optional: false,
is_field: false,
};
parse_path_segment(type_path, &seg, fld)
},
| _ => Err(Error::UnsupportedFieldType(
f.ty.span(),
format!("{:?}", f.ty),
)),
}
}
fn format_path(ts: &TokenStream) -> String {
ts.to_string().replace(' ', "")
}
fn parse_path_segment(
type_path: &syn::TypePath,
seg: &syn::PathSegment,
f: Field,
) -> Result<Field, Error> {
let s = seg.ident.clone().to_string();
match s.as_str() {
| "Option" => parse_field_option(type_path, seg, f),
| "NodeId" => parse_field_nodeid(type_path, seg, f),
| "EnumNodeId" => parse_field_enum_nodeid(type_path, seg, f),
| "Vec" => parse_field_vec(type_path, seg, f),
| "Field" => parse_field_field(type_path, seg, f),
| "Enum" => parse_field_enum(type_path, seg, f),
| "Spanned" => parse_field_spanned(type_path, seg, f),
| "crate" => Ok(Field {
ty: FieldType::Path(format_path(&type_path.to_token_stream())),
..f
}),
| "String" => parse_field_string(type_path, seg, f),
| "Span" => Ok(Field {
ty: FieldType::Span,
..f
}),
| "Ident" => Ok(Field {
ty: FieldType::Ident,
..f
}),
| "bool" => parse_field_literal(type_path, seg, f, "bool"),
| "usize" => parse_field_literal(type_path, seg, f, "usize"),
| "u8" => parse_field_literal(type_path, seg, f, "u8"),
| "u16" => parse_field_literal(type_path, seg, f, "u16"),
| "u32" => parse_field_literal(type_path, seg, f, "u32"),
| "u64" => parse_field_literal(type_path, seg, f, "u64"),
| "u128" => parse_field_literal(type_path, seg, f, "u128"),
| "isize" => parse_field_literal(type_path, seg, f, "isize"),
| "i8" => parse_field_literal(type_path, seg, f, "i8"),
| "i16" => parse_field_literal(type_path, seg, f, "i16"),
| "i32" => parse_field_literal(type_path, seg, f, "i32"),
| "i64" => parse_field_literal(type_path, seg, f, "i64"),
| "i128" => parse_field_literal(type_path, seg, f, "i128"),
| "f32" => parse_field_literal(type_path, seg, f, "f32"),
| "f64" => parse_field_literal(type_path, seg, f, "f64"),
| _ => {
let type_name: &'static str =
Box::leak(seg.ident.to_string().into_boxed_str());
parse_field_literal(type_path, seg, f, type_name)
},
}
}
fn parse_field_nodeid(
_type_path: &syn::TypePath,
seg: &syn::PathSegment,
f: Field,
) -> Result<Field, Error> {
if seg.ident.to_string().as_str() != "NodeId" {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected NodeId to have type arguments".to_string(),
));
}
let field_ty = match &seg.arguments {
| syn::PathArguments::AngleBracketed(args) => {
let args_len = args.args.len();
match args_len {
| 0 => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected NodeId to have type arguments".to_string(),
));
},
| 1 => {
match args.args.first().unwrap() {
| syn::GenericArgument::Type(ty) => {
FieldType::SingleNode(format_path(&ty.to_token_stream()))
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected NodeId to have type arguments".to_string(),
));
},
}
},
| _ => {
let mut tys = Vec::with_capacity(args_len);
for arg in args.args.iter() {
match arg {
| syn::GenericArgument::Type(ty) => {
tys.push(format_path(&ty.to_token_stream()));
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected NodeId to have type arguments".to_string(),
));
},
}
}
FieldType::MultipleNode(tys)
},
}
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected NodeId to have at least one type argument".to_string(),
));
},
};
Ok(Field { ty: field_ty, ..f })
}
fn parse_field_enum_nodeid(
_type_path: &syn::TypePath,
seg: &syn::PathSegment,
f: Field,
) -> Result<Field, Error> {
if seg.ident.to_string().as_str() != "EnumNodeId" {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected NodeId to have type arguments".to_string(),
));
}
let field_ty = match &seg.arguments {
| syn::PathArguments::AngleBracketed(args) => match args.args.len() {
| 0 => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected EnumNodeId to have type argument".to_string(),
));
},
| 1 => match args.args.first().unwrap() {
| syn::GenericArgument::Type(ty) => {
FieldType::EnumNodeId(format_path(&ty.to_token_stream()))
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected EnumNodeId to have type arguments".to_string(),
));
},
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected EnumNodeId to have only one type argument".to_string(),
));
},
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected EnumNodeId to have one type argument".to_string(),
));
},
};
Ok(Field { ty: field_ty, ..f })
}
fn parse_field_field(
_type_path: &syn::TypePath,
seg: &syn::PathSegment,
f: Field,
) -> Result<Field, Error> {
if seg.ident.to_string().as_str() != "Field" {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected Field to have type arguments".to_string(),
));
}
let field = match &seg.arguments {
| syn::PathArguments::AngleBracketed(args) => match args.args.len() {
| 0 => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected Field to have type arguments".to_string(),
));
},
| 1 => match args.args.first().unwrap() {
| syn::GenericArgument::Type(ty) => match ty {
| Type::Path(p) => {
let mut segments = p.path.segments.clone().into_iter();
let seg = segments
.next()
.expect("Expected Type::Path to have at least one segment");
parse_path_segment(p, &seg, f)?
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Invalid argument type for 'Field'".to_string(),
));
},
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected Field to have type arguments".to_string(),
));
},
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected Field to have only on type argument".to_string(),
));
},
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected Field to have at least one type argument".to_string(),
));
},
};
Ok(Field {
is_field: true,
..field
})
}
fn parse_field_enum(
_type_path: &syn::TypePath,
seg: &syn::PathSegment,
f: Field,
) -> Result<Field, Error> {
if seg.ident.to_string().as_str() != "Enum" {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected Field to have type arguments".to_string(),
));
}
let field_ty = match &seg.arguments {
| syn::PathArguments::AngleBracketed(args) => match args.args.len() {
| 0 => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected NodeId to have type arguments".to_string(),
));
},
| 1 => match args.args.first().unwrap() {
| syn::GenericArgument::Type(ty) => {
FieldType::Enum(format_path(&ty.to_token_stream()))
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected NodeId to have type arguments".to_string(),
));
},
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected Enum to have only one type arguments".to_string(),
));
},
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected NodeId to have at least one type argument".to_string(),
));
},
};
Ok(Field { ty: field_ty, ..f })
}
fn parse_field_vec(
_type_path: &syn::TypePath,
seg: &syn::PathSegment,
f: Field,
) -> Result<Field, Error> {
if seg.ident.to_string().as_str() != "Vec" {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected Vec".to_string(),
));
}
let field = match &seg.arguments {
| syn::PathArguments::AngleBracketed(args) => match args.args.len() {
| 0 => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Vec must have at least one type argument".to_string(),
));
},
| 1 => match args.args.first().unwrap() {
| syn::GenericArgument::Type(ty) => match ty {
| Type::Path(p) => {
let mut segments = p.path.segments.clone().into_iter();
let seg = segments
.next()
.expect("Expected Type::Path to have at least one segment");
parse_path_segment(p, &seg, f)?
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Invalid argument type for 'Vec'".to_string(),
));
},
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected Vec to have type arguments".to_string(),
));
},
},
| _ => Field {
ty: FieldType::MultipleNode(
args
.args
.iter()
.map(|a| format_path(&a.to_token_stream()))
.collect(),
),
..f
},
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Vec must have at least one type argument".to_string(),
));
},
};
Ok(Field {
is_vec: true,
..field
})
}
fn parse_field_option(
type_path: &syn::TypePath,
seg: &syn::PathSegment,
f: Field,
) -> Result<Field, Error> {
if seg.ident.to_string().as_str() != "Option" {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Invalid field type: expected 'Option'".to_string(),
));
}
let field = match &seg.arguments {
| syn::PathArguments::AngleBracketed(args) => match args.args.len() {
| 0 => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"No arguments provided for 'Option' type".to_string(),
));
},
| 1 => match args.args.first().unwrap() {
| syn::GenericArgument::Type(ty) => match ty {
| Type::Path(p) => {
let mut segments = p.path.segments.clone().into_iter();
let seg = segments
.next()
.expect("Expected Type::Path to have at least one segment");
parse_path_segment(type_path, &seg, f)?
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Invalid argument type for 'Option'".to_string(),
));
},
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Invalid generic argument for 'Option'".to_string(),
));
},
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Invalid number of arguments for 'Option'".to_string(),
));
},
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Invalid path arguments: expected 'AngleBracketed'".to_string(),
));
},
};
Ok(Field {
is_optional: true,
..field
})
}
fn parse_field_string(
_type_path: &syn::TypePath,
seg: &syn::PathSegment,
f: Field,
) -> Result<Field, Error> {
if seg.ident.to_string().as_str() != "String" {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Invalid field type: expected 'String'".to_string(),
));
}
if !seg.arguments.is_empty() {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"String does not take any arguments".to_string(),
));
}
Ok(Field {
ty: FieldType::String,
..f
})
}
fn parse_field_literal(
_type_path: &syn::TypePath,
seg: &syn::PathSegment,
f: Field,
lit_ty: &'static str,
) -> Result<Field, Error> {
if !seg.arguments.is_empty() {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"literal types do not take any arguments".to_string(),
));
}
Ok(Field {
ty: FieldType::Literal(lit_ty),
..f
})
}
fn parse_field_spanned(
_type_path: &syn::TypePath,
seg: &syn::PathSegment,
f: Field,
) -> Result<Field, Error> {
if seg.ident.to_string().as_str() != "Spanned" {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected Spanned".to_string(),
));
}
let field_ty = match &seg.arguments {
| syn::PathArguments::AngleBracketed(args) => match args.args.len() {
| 0 => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected Spanned to have type argument".to_string(),
));
},
| 1 => match args.args.first().unwrap() {
| syn::GenericArgument::Type(syn::Type::Path(p)) => {
let inner_seg = p.path.segments.first();
if let Some(inner_seg) = inner_seg {
if inner_seg.ident.to_string().as_str() == "Ident" {
FieldType::SpannedIdent
} else {
return Err(Error::InvalidFieldTypeMessage(
inner_seg.__span(),
format!(
"Expected Spanned<Ident>, found Spanned<{}>",
inner_seg.ident
),
));
}
} else {
return Err(Error::InvalidFieldTypeMessage(
args.__span(),
"Expected Spanned<Ident>".to_string(),
));
}
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected Spanned<Ident>".to_string(),
));
},
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Spanned only takes one type argument".to_string(),
));
},
},
| _ => {
return Err(Error::InvalidFieldTypeMessage(
seg.__span(),
"Expected Spanned to have type argument".to_string(),
));
},
};
Ok(Field { ty: field_ty, ..f })
}