Как я могу обработать или проверить, является ли тип Option в макросе Rust? - PullRequest
1 голос
/ 17 октября 2019

Я пытаюсь создать макрос для создания структуры, которая может быть заполнена из базы данных postgres. Теперь, поскольку в базе данных есть поля, которые можно обнулять и не обнулять, я бы хотел обрабатывать их по-разному в макросе.

Вывод должен иметь такую ​​структуру:

#[derive(Debug, Default)]
pub struct MyStruct {
    pub attrib_a: i64,
    pub attrib_b: Option<i64>,
}

impl MyStruct {
    pub fn get_row(&self, row: &postgres::rows::Row) -> MyStruct {
        MyStruct {
            // the non-nullable attrib_a, where I can for sure take the value out of the Option and assign it
            attrib_a: match row.get::<_, Option<i64>>("attrib_a") {
                Some(x) => x,
                None => 0,
            },

            // here for the nullable attrib_b I just want to return the Option as is
            attrib_b: row.get::<_, Option<i64>>("attrib_b"),
        }
    }
}

Вот текущий код макроса:

macro_rules! make_table_struct {
    ($tname: stmt => $sname: ident; $($fname: ident: $ftype: ty),+) => {

        #[derive(Debug, Clone, Default)]
        pub struct $sname {
            $(
                pub $fname: $ftype,
            )+
        }

        impl $sname
        {
            pub fn get_row(&self,row:&postgres::rows::Row)->$sname
            {
                $sname
                {
                    //How do I know if I have an Option here or not and act then accordingly?
                    $(
                        $fname: row.get::<_,Option<$ftype>>(stringify!($fname)),
                    )+
                }
            }
        }
    }
}

Макро-вызов:

make_table_struct! ("source_table_name" => MyStruct; attrib_a: i64, attrib_b: Option<i64>)

1 Ответ

3 голосов
/ 18 октября 2019

Вот макрос, который принимает $fname и тип, который может быть необязательным. Если тип является необязательным, он генерирует другую функцию, которая, если это не так:

macro_rules! make_get_row {
    ($fname: ident, Option<$ftype: ty>) => {
        fn $fname(i: Option<$ftype>) -> i64 {
            i.unwrap_or(0)
        }
    };
    ($fname: ident, $ftype: ty) => {
        fn $fname(i: $ftype) -> i64 {
            i
        }
    };
}

make_get_row!(direct, i64);
make_get_row!(via_op, Option<i64>);

fn main() {
    direct(1);
    via_op(Some(1));
}

Вы можете использовать (возможно скорректированный) вариант этого макроса в пределах make_table_struct.

Следующее не качество продукции, но может привести вас куда-то:

macro_rules! get_table_row {
    ($row: ident, nonnullable $fname:ident: $ftype:ty) => {
        $row.get::<_,$ftype>(stringify!($fname))
    };
    ($row: ident, nullable $fname:ident: $ftype:ty) => {
        match $row.get::<_,Option<Option<$ftype>>>(stringify!($fname)) { Some(x) => x, None => Some(0) }
    }
}
type nullable<T> = Option<T>;
type nonnullable<T> = T;
macro_rules! make_table_struct {
    ($tname:stmt => $sname:ident; $($nul: ident $fname:ident: $ftype:tt),+) => {

        #[derive(Debug, Clone, Default)]
        pub struct $sname {
            $(
                pub $fname: $nul < $ftype >,
            )+
        }

        impl $sname {
            pub fn get_row(&self, row: &postgres::rows::Row) -> $sname {
                $(
                    let $fname = get_table_row!(row, $nul $fname: $ftype);
                )+
                $sname {
                    $($fname,)+
                }
            }
        }
    }
}

make_table_struct! ("source_table_name" => MyStruct; nonnullable attrib_a: i64, nullable attrib_b: i64);
...