根据字段类型有条件地生成 From impl

Conditionally generate a From impl based on field type

我正在尝试创建一个通用实现,用于根据不同的字段类型生成 From/Into。

Link to Playground

我发现了以下问题:

error[E0425]: cannot find value `item` in this scope
  --> src/lib.rs:23:21
   |
23 |             $param: item.$param,
   |                     ^^^^ not found in this scope
...
65 | create_impl! { TargetStruct, InputStruct, { field1: Option<String>, field2: Option<Uuid> }}
   | ------------------------------------------------------------------------------------------- in this macro invocation

任何人都可以为我指出正确的方向,告诉我如何让它工作/如果可能的话。在这一点上,我不确定如何将输入参数传递给规则。

谢谢!


#[macro_use]
macro_rules! create_impl {
    ( @ $target:ident, $input:ident, { } -> ($($result:tt)*) ) => (
      impl From<$input> for $target {
          fn from(item: $input) -> Self {
            Self {
              $($result)*
              ..Default::default()
            }
        }
    });

    ( @ $target:ident, $input:ident, { $param:ident : Option<Uuid>, $($rest:tt)* } -> ($($result:tt)*) ) => (
        create_impl!(@ $target, $input, { $($rest)* } -> (
            $($result)*
            $param: item.$param.map(|v| v.to_string()),
        ));
    );

    ( @ $target:ident, $input:ident, { $param:ident : $type:ty, $($rest:tt)* } -> ($($result:tt)*) ) => (
        create_impl!(@ $target, $input, { $($rest)* } -> (
            $($result)*
            $param: item.$param,
        ));
    );


    ( @ $target:ident, $input:ident, { $param:ident : $type:ty, $($rest:tt)* } -> ($($result:tt)*) ) => (
        create_impl!(@ $target, $input, { $($rest)* } -> (
            $($result)*
            $param: item.$param,
        ));
    );

    ( $target:ident, $input:ident, { $( $param:ident : $type:ty ),* $(,)* } ) => (
        create_impl!(@ $target, $input, { $($param : $type,)* } -> ());
    );
}

您可以让 Rust 完成繁重的工作:

macro_rules! memberize_result {
    ($item: ident, $param:ident : Option<Uuid>) => (
        $item.$param.map(|v| v.to_string())
    );

    ($item: ident, $param:ident : Option<String>) => (
        $item.$param
    );
}

#[macro_use]
macro_rules! create_impl {
    ( $target:ident, $input:ident, { $( $param:ident : ($($type:tt)*) ),* $(,)* } ) => (
        impl From<$input> for $target {
            fn from(item: $input) -> Self {
                Self {
                    $($param: memberize_result!(item, $param : $($type)*),)*
                    ..Default::default()
                }
            }
        }
    );
}


use uuid::Uuid; // 0.8.1

#[derive(Default)]
pub struct InputStruct {
    pub field1: Option<String>,
    pub field2: Option<Uuid>,
}

#[derive(Default)]
pub struct TargetStruct {
    pub field1: Option<String>,
    pub field2: Option<String>,
}

// Trying to turn this into a macro
// impl From<ExampleStruct> for TargetStruct {
//     fn from(item: ExampleStruct) -> Self {
//         let mut res = Self::default();
//         res.field1 = item.field1;
//         res.field2 = item.field2.map(|v| v.to_string());
//         res
//     }
// }

create_impl! { TargetStruct, InputStruct, { field1: (Option<String>), field2: (Option<Uuid>) }}