use proc_macro2::TokenStream;
use quote::quote;
use syn::{Field, GenericArgument, PathArguments, Type, TypePath};
use crate::{fields::getter_data::GetterData, modifier::Modifier};
const OPTION_NAME: &str = "Option";
enum OptionReturnMethod {
AsRef,
AsMut,
None,
}
impl From<&Modifier> for OptionReturnMethod {
fn from(value: &Modifier) -> Self {
match value {
Modifier::Move => OptionReturnMethod::None,
Modifier::Ref => OptionReturnMethod::AsRef,
Modifier::MutRef => OptionReturnMethod::AsMut,
}
}
}
impl OptionReturnMethod {
fn method_token_stream(&self) -> TokenStream {
match self {
OptionReturnMethod::AsRef => quote! {.as_ref()},
OptionReturnMethod::AsMut => quote! {.as_mut()},
OptionReturnMethod::None => quote! {},
}
}
}
pub fn is_option(ty: &Type) -> bool {
if let Type::Path(TypePath { qself: None, path }) = ty {
if let Some(seg) = path.segments.last() {
return seg.ident == OPTION_NAME;
}
}
false
}
fn option_inner_type(ty: &Type) -> Option<&Type> {
if let Type::Path(TypePath { qself: None, path }) = ty {
if let Some(seg) = path.segments.last() {
if seg.ident == OPTION_NAME {
if let PathArguments::AngleBracketed(ref ab) = seg.arguments {
if let Some(GenericArgument::Type(inner_ty)) = ab.args.first() {
return Some(inner_ty);
}
}
}
}
}
None
}
pub fn preprocess_option_getter(field: &Field, self_modifier: &Modifier) -> GetterData {
let field_name = field
.ident
.as_ref()
.expect("Works only for regular structs, not union-structs");
let inner_type = option_inner_type(&field.ty).expect("Could not read the `Option` inner type");
let result_type = match self_modifier {
Modifier::Move => quote! { Option<#inner_type> },
Modifier::Ref => quote! { Option<& #inner_type> },
Modifier::MutRef => quote! { Option<&mut #inner_type> },
};
let method: OptionReturnMethod = self_modifier.into();
let method_tk = method.method_token_stream();
GetterData {
return_type: result_type,
return_body: quote! {self. #field_name #method_tk},
}
}