mod token_ref_info;
use crate::common;
use quote::quote;
use syn::{Data, Field, Type};
pub use token_ref_info::*;
pub fn impl_referenceable_macro(ast: &syn::DeriveInput) -> proc_macro2::TokenStream {
let name = &ast.ident;
let fields = common::get_struct_enum_fields(ast);
match ast.data {
Data::Struct(_) => {
let self_reference = get_self_reference_token(&fields)
.expect("There is no `#[referenceable(self_reference)]` set.");
let self_ref_ident = self_reference.ident.clone().unwrap();
let self_ref_type = unwrap_option_type(self_reference);
match self_ref_type {
Type::Tuple(_) => {
let self_ref_index = find_self_ref_in_tuple(&self_ref_type);
quote! {
impl Referenceable for #name {
fn get_reference(&self) -> Option<df_ls_core::ReferenceTo<Self>> {
match &self.#self_ref_ident {
Some(self_ref) => Some(self_ref.#self_ref_index.clone()),
None => None,
}
}
fn get_ref_type() -> &'static str {
stringify!(#name)
}
}
}
}
Type::Path(_) => {
quote! {
impl Referenceable for #name {
fn get_reference(&self) -> Option<df_ls_core::ReferenceTo<Self>> {
self.#self_ref_ident.clone()
}
fn get_ref_type() -> &'static str {
stringify!(#name)
}
}
}
}
_ => panic!("This type is not supported for Referenceable."),
}
}
Data::Enum(_) => {
let match_items = {
let mut parse_gen = quote! {};
for (_i, field) in fields.iter().enumerate() {
let ident = field.ident.as_ref().unwrap();
parse_gen = quote! {
#parse_gen
Self::#ident(x) => {
match x.get_reference() {
Some(reference) => Some(ReferenceTo::new(reference.0)),
None => None,
}
},
};
}
parse_gen
};
quote! {
impl Referenceable for #name {
fn get_reference(&self) -> Option<df_ls_core::ReferenceTo<Self>> {
match self{
#match_items
}
}
fn get_ref_type() -> &'static str {
stringify!(#name)
}
}
}
}
Data::Union(_) => {
unimplemented!("Union is not implemented for Referenceable.");
}
}
}
fn get_self_reference_token(struct_fields: &[Field]) -> Option<Field> {
let mut result = None;
for field in struct_fields.iter() {
let token_info = get_token_ref_info(field);
if token_info.self_reference {
if result.is_none() {
result = Some(field.clone());
} else {
panic!("ERROR: Only one primary_token can be set.");
}
}
}
result
}
fn unwrap_option_type(field: Field) -> Type {
if let Type::Path(path) = field.ty {
for path_segment in path.path.segments {
if path_segment.ident == "Option" {
if let syn::PathArguments::AngleBracketed(args) = path_segment.arguments {
for generic_arg in args.args {
if let syn::GenericArgument::Type(ty) = generic_arg {
return ty;
}
}
}
}
}
}
panic!("This type is not supported for Referenceable.");
}
fn find_self_ref_in_tuple(ty: &Type) -> syn::Index {
if let Type::Tuple(tuple) = ty {
for (index, item) in tuple.elems.iter().enumerate() {
if is_self_ref(item) {
return syn::Index::from(index);
}
}
}
panic!("Internal error: Expected Tuple, found something else.");
}
fn is_self_ref(ty: &Type) -> bool {
if let Type::Path(path) = ty {
for segment in &path.path.segments {
if segment.ident == "ReferenceTo" {
if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
for generic_arg in &args.args {
if let syn::GenericArgument::Type(ty) = generic_arg {
return is_self(ty);
}
}
}
}
}
}
false
}
fn is_self(ty: &Type) -> bool {
if let Type::Path(path) = ty {
for segment in &path.path.segments {
if segment.ident == "Self" {
return true;
}
}
}
false
}