trl_codegen 1.2.1

This library provides auto generation of some common methods based on Rust macros
Documentation
//! # option
//!
//! Perform operations on a field of type `Option`
//!
//! e. g. if we have field of type `Option<T>` we want the getter to return `Option<&T>`, not the `&Option<T>`

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
}

/// Generates the return type and body of the getter with `Option` return type
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},
    }
}