>>107136612
Just write a simple macro
macro_rules! with_defaults {
(fn $name:ident( $($aname:ident : $atype:ty $(= $aval:expr )? ),* ) $(-> $ret:ty)? $body:block) => {
fn $name(args: impl Into<${concat(__,$name,Args)}>) $(-> $ret)? {
let ${concat(__,$name,Args)}{ $($aname,)* } = args.into();
$(let $aname = $aname $(.unwrap_or_else(|| $aval))? ; )*
$body
}
#[allow(non_camel_case_types)]
struct ${concat(__,$name,Args)} {
$($aname: with_defaults!(@if($($aval)?) { Option<$atype> } else { $atype }), )*
}
with_defaults!{@gen $name () () ($( $aname : $atype $(= $aval )?, )*)}
};
(@if($($arg:tt)+) { $($then:tt)* } else { $($else:tt)* } ) => { $($then)* };
(@if() { $($then:tt)* } else { $($else:tt)* } ) => { $($else)* };
(@gen $name:ident ($($sname:ident : $stype:ty,)*) ($($uname:ident : $utype:ty,)*) ()) => {
impl From<($($stype,)*)> for ${concat(__,$name,Args)} {
fn from(($($sname,)*): ($($stype,)*)) -> Self {
${concat(__,$name,Args)} { $($sname: $sname.into(),)* $($uname: None,)* }
}
}
};
(@gen $name:ident ($($set:tt)*) ($($unset:tt)*) ($nname:ident : $ntype:ty, $($rest:tt)*)) => {
with_defaults!{@gen $name ($($set)* $nname : $ntype,) ($($unset)*) ($($rest)*) }
};
(@gen $name:ident ($($set:tt)*) ($($unset:tt)*) ($nname:ident : $ntype:ty = $nval:expr, $($rest:tt)*)) => {
with_defaults!{@gen $name ($($set)*) ($($unset)* $nname : $ntype,) ($($rest)*) }
with_defaults!{@gen $name ($($set)* $nname : $ntype,) ($($unset)*) ($($rest)*) }
};
}
with_defaults!{
fn kek(a: i32, b: bool, c: f32 = 0.5, d: String = "foo".to_string()) {
let g = a;
println!("{a} {b} {c} {d}");
}
}
#[test]
fn test_kek() {
kek((1, true, 3.14, "hello world".to_string()));
// 1 true 3.14 hello world
kek((1, true))
// 1 true 0.5 foo
}