#![allow(dead_code)]
extern crate proc_macro;
use std::fmt::Display;
use proc_macro2::Span;
use syn::__private::ToTokens;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::token::Comma;
use syn::{
parse, Data, DataStruct, DeriveInput, Field, Fields, GenericArgument, Path, PathArguments, Type,
};
pub const BUILTIN_TYPE_OPTION: &str = "Option";
pub const BUILTIN_TYPE_VEC: &str = "Vec";
pub fn try_derive_input(input: proc_macro::TokenStream) -> DeriveInput {
parse(input).unwrap()
}
#[rustfmt::skip]
pub fn try_parse_named_fields(input: &DeriveInput) -> &Punctuated<Field, Comma> {
let struct_name = &input.ident;
match &input.data {
Data::Struct(data) => match &data.fields {
Fields::Named(fields) => &fields.named,
_ => panic!(
"synext: Does not contain named fields! target:`{}`",
struct_name
),
},
_ => panic!(
"synext: Only structs are supported! target:`{}`",
struct_name
),
}
}
#[rustfmt::skip]
pub fn try_parse_unnamed_fields(input: &DeriveInput) -> &Punctuated<Field, Comma> {
let struct_name = &input.ident;
match &input.data {
Data::Struct(data) => match &data.fields {
Fields::Unnamed(fields) => &fields.unnamed,
_ => panic!(
"synext: Does not contain unnamed fields! target:`{}`",
struct_name
),
},
_ => panic!(
"synext: Only structs are supported! target:`{}`",
struct_name
),
}
}
#[rustfmt::skip]
pub fn try_match_fields(input: &DeriveInput) -> &Punctuated<Field, Comma> {
let struct_name = &input.ident;
match &input.data {
Data::Struct(DataStruct {
fields: Fields::Named(fields),
..
}) => &fields.named,
Data::Struct(DataStruct {
fields: Fields::Unnamed(fields),
..
}) => &fields.unnamed,
_ => panic!(
"synext: Does not contain any fields! target:`{}`",
struct_name
),
}
}
pub fn try_unwrap_option(ty: &Type) -> &Type {
try_unwrap_types(BUILTIN_TYPE_OPTION, 1, ty).unwrap()[0]
}
pub fn try_unwrap_vec(ty: &Type) -> &Type {
try_unwrap_types(BUILTIN_TYPE_VEC, 1, ty).unwrap()[0]
}
#[rustfmt::skip]
pub fn try_unwrap_types<'a>(
ident: &str,
target_types: usize,
ty: &'a Type,
) -> Option<Vec<&'a Type>> {
if let Type::Path(
syn::TypePath {
ref path,
..
}) = ty {
if try_predicate_is_ident(&ident, &path) && try_predicate_path_segments_is_not_empty(path) {
let inner_type = try_extract_inner_types(ty);
let mut len = 0;
if let Some(ref inner) = inner_type {
len = inner.len()
}
if len == target_types {
return inner_type;
} else {
panic!("synext: Type `{}` has more inner Types then expected! (expected: {} | got: {})", ident, target_types, len);
}
}
if try_predicate_is_not_ident(&ident, &path) {
let res_ident = path.get_ident();
if let Some(res_ident) = res_ident {
panic!("synext: Expected Type `{:?}`, got `{:?}`", ident, res_ident);
} else {
panic!("synext: Expected Type `{:?}`, but has no type!", ident);
}
}
}
None
}
#[rustfmt::skip]
pub fn try_extract_inner_types(ty: &Type) -> Option<Vec<&Type>> {
if let Type::Path(
syn::TypePath {
ref path,
..
}) = ty {
if try_predicate_path_segments_is_not_empty(path) {
if let PathArguments::AngleBracketed(ref bracketed_generics) =
path.segments.last().unwrap().arguments
{
let mut ty_vec = Vec::new();
for generic in bracketed_generics.args.iter() {
if let GenericArgument::Type(ref ty) = generic {
ty_vec.push(ty);
}
}
if !ty_vec.is_empty() {
return Some(ty_vec);
}
}
}
}
None
}
#[rustfmt::skip]
pub fn try_extract_field_attribute_path_attribute(derive_attribute: &str, path_attribute: &str, field: &Field) -> syn::Result<Option<syn::Ident>> {
for attr in &field.attrs {
if let Ok(
syn::Meta::List(
syn::MetaList {
ref path,
ref nested,
..
})) = attr.parse_meta()
{
if let Some(p) = path.segments.first() {
if p.ident == derive_attribute {
if let Some(syn::NestedMeta::Meta(syn::Meta::NameValue(kv))) = nested.first() {
if kv.path.is_ident(path_attribute) {
if let syn::Lit::Str(ref target_attr) = kv.lit {
return Ok(Some(syn::Ident::new(
target_attr.value().as_str(),
attr.span(),
)));
}
} else {
if let Ok(syn::Meta::List(ref list)) = attr.parse_meta() {
return Err(syn::Error::new_spanned(
list,
format!(
r#"expected `{}({} = "...")`"#,
derive_attribute, path_attribute
),
));
}
}
}
}
}
}
}
Ok(None)
}
pub fn make_new_compile_error<T: Display>(span: Span, message: T) -> proc_macro::TokenStream {
syn::Error::new(span, message).to_compile_error().into()
}
pub fn make_new_spanned_compile_error<T: ToTokens, U: Display>(
tokens: T,
message: U,
) -> proc_macro::TokenStream {
syn::Error::new_spanned(tokens, message)
.to_compile_error()
.into()
}
pub fn try_predicate_is_not_option_and_vec(ty: &Type) -> bool {
try_predicate_is_not_option(ty) && try_predicate_is_not_vec(ty)
}
pub fn try_predicate_is_not_option(ty: &Type) -> bool {
!try_predicate_is_option(ty)
}
pub fn try_predicate_is_not_vec(ty: &Type) -> bool {
!try_predicate_is_vec(ty)
}
pub fn try_predicate_is_option(ty: &Type) -> bool {
try_predicate_is_type(BUILTIN_TYPE_OPTION, 1, ty)
}
pub fn try_predicate_is_vec(ty: &Type) -> bool {
try_predicate_is_type(BUILTIN_TYPE_VEC, 1, ty)
}
#[rustfmt::skip]
pub fn try_predicate_is_type(ident: &str, target_types: usize, ty: &Type) -> bool {
if let Type::Path(
syn::TypePath {
ref path,
..
}) = ty {
if try_predicate_is_ident(&ident, &path) && path.segments.len() == target_types {
return true;
}
}
false
}
pub fn try_predicate_is_not_ident(ident: &str, path: &Path) -> bool {
!try_predicate_is_ident(ident, path)
}
pub fn try_predicate_is_ident(ident: &str, path: &Path) -> bool {
try_predicate_path_segments_is_not_empty(path) && path.segments.last().unwrap().ident == ident
}
pub fn try_predicate_path_segments_is_not_empty(path: &Path) -> bool {
!try_predicate_path_segments_is_empty(path)
}
pub fn try_predicate_path_segments_is_empty(path: &Path) -> bool {
path.segments.is_empty()
}