I am trying to create a macro to generate a struct which can be filled from a postgres-database. Now, as there are fields in the db which are nullable and non-nullable, I'd want to handle them differently in the macro.
Output should be a struct like this:
#[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"),
}
}
}
Here's the current macro code:
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)),
)+
}
}
}
}
}
Macro-call:
make_table_struct! ("source_table_name" => MyStruct; attrib_a: i64, attrib_b: Option<i64>)
Here's a macro that takes a $fname
and a type, which may be optional. If the type is optional, it generates another function than if is not:
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));
}
You should be able to use a (possibly adjusted) variant of this macro within make_table_struct
.
The following is not production quality, but might lead you somewhere:
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);