Skip to content

[Rust] Make it more convenient for users of the ext type. #2726

@urlyy

Description

@urlyy

Feature Request

currently, when users want to use EXT, because we need to handle Any, some fixed code has to be implemented manually by users.
Here is a demo, and as_any and fory_default are some fixed code.

#[derive(Debug, PartialEq, Default)]
struct ExtItem {
    id: i32,
}
impl ForyDefault for ExtItem {
    fn fory_default() -> Self {
        Self::default()
    }
}
impl Serializer for ExtItem {
    fn fory_write_data(&self, context: &mut WriteContext, is_field: bool) {
        write_data(&self.id, context, is_field);
    }
    fn fory_read_data(context: &mut ReadContext, is_field: bool) -> Result<Self, Error> {
        Ok(Self {
            id: read_data(context, is_field)?,
        })
    }
    
    fn fory_type_id_dyn(&self, fory: &Fory) -> u32 {
        Self::fory_get_type_id(fory)
    }
    
    fn as_any(&self) -> &dyn std::any::Any {
        self
    }
}

This is inconvenient. But now I haven't find how to set default implementation for as_any before runtime. And impl ForyDefault must be implemented manually. If all else cannot work, we can give users a helper macro.

Describe the solution you'd like

Here is a helper macro implementation.

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemImpl};

#[proc_macro_attribute]
pub fn ext_helper(_attr: TokenStream, item: TokenStream) -> TokenStream {
    // 解析原来的 impl 块
    let input = parse_macro_input!(item as ItemImpl);
    let self_ty = &input.self_ty;
    let mut new_items = input.items.clone();
    // 内部插入函数 as_any
    let injected_inside = syn::parse_quote! {
        fn as_any(&self) -> &dyn std::any::Any {
            self
        }
    };
    new_items.push(injected_inside);
    // 生成原 impl + 内部注入函数
    let original_impl = ItemImpl {
        items: new_items,
        ..input.clone()
    };

    // 生成 impl ForyDefault for <Type>
    let fory_default_impl = quote! {
        impl ForyDefault for #self_ty {
            fn fory_default() -> Self {
                Self::default()
            }
        }
    };

    let expanded = quote! {
        #original_impl
        #fory_default_impl
    };

    TokenStream::from(expanded)
}

and use it as:

use my_macro::ext_helper;

trait Serializer {
    fn serialize(&self);
}

struct Foo;
#[ext_helper]
impl Serializer for Foo {
    fn serialize(&self) {}
}

fn main() {
    
}

the expanded code is:

use my_macro::ext_helper;
trait Serializer {
    fn serialize(&self);
}
struct Foo;
impl Serializer for Foo {
    fn serialize(&self) {}
    fn as_any(&self) -> &dyn std::any::Any {
        self
    }
}
impl ForyDefault for Foo {
    fn fory_default() -> Self {
        Self::default()
    }
}
fn main() {}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions