#![cfg_attr(docsrs, feature(doc_cfg))]
#[allow(unused_imports)]
#[doc(hidden)]
pub use paste::paste as __private_codegen_paste;
#[macro_export]
macro_rules! flatten_structs {
(
$(#[$struct_attr:meta])*
$vis:vis
struct
$name:ident {$(
$(#[$($field_attr:tt)*])*
$field_vis:vis $field_name:ident: $field_type:path
),* $(,)? }
) => {
$crate::flatten_structs!{@gather_fields
expanded_fields = {},
queued_fields = { $({
$(#[$($field_attr)*])*
$field_vis $field_name: $field_type
})* },
cx = {
definition = {
$(#[$struct_attr])*
$vis
struct
$name
},
dollar = { $ },
},
}
};
(@gather_fields
expanded_fields = $expanded_fields:tt,
queued_fields = { {
$(#[doc = $($field_docs:tt)*])*
#[flatten]
$(#[$($field_attr:tt)*])*
$field_vis:vis $field_name:ident: $field_type:path
} $($queued_fields:tt)* },
cx = $cx:tt,
) => {
$field_type!{
call = { $crate::flatten_structs },
prefix = { @callback },
cx = {
flattened_vis = { $field_vis },
expanded_fields = $expanded_fields,
queued_fields = { $($queued_fields)* },
cx = $cx,
},
}
};
(@callback
fields = {$(
$(#[$($field_attr:tt)*])*
$field_vis:vis $field_name:ident: $field_type:path,
)*},
cx = {
flattened_vis = { $flattened_vis:vis },
expanded_fields = { $($expanded_fields:tt)* },
queued_fields = $queued_fields:tt,
cx = $cx:tt,
},
) => {
$crate::flatten_structs!{@gather_fields
expanded_fields = { $($expanded_fields)* $({
$(#[$($field_attr)*])*
$flattened_vis $field_name: $field_type
})*},
queued_fields = $queued_fields,
cx = $cx,
}
};
(@gather_fields
expanded_fields = { $($expanded_fields:tt)* },
queued_fields = { {
$(#[$($field_attr:tt)*])*
$field_vis:vis $field_name:ident: $field_type:path
} $($queued_fields:tt)* },
cx = $cx:tt,
) => {
$crate::flatten_structs!{@gather_fields
expanded_fields = { $($expanded_fields)* {
$(#[$($field_attr)*])*
$field_vis $field_name: $field_type
}},
queued_fields = { $($queued_fields)* },
cx = $cx,
}
};
(@gather_fields
expanded_fields = { $({
$(#[$field_attr:meta])*
$field_vis:vis $field_name:ident: $field_type:path
})* },
queued_fields = {},
cx = {
definition = {
$(#[$struct_attr:meta])*
$vis:vis
struct
$name:ident
},
dollar = { $dollar:tt },
},
) => {
$(#[$struct_attr])*
$vis struct $name {$(
$(#[$field_attr])*
$field_vis $field_name: $field_type,
)*}
$crate::__private_codegen_paste!{
macro_rules! [<__private_field_inspect_for $name>] {
(
call = { $dollarcall:path },
prefix = { $dollar($dollarprefix:tt)* },
cx = $dollarcx:tt,
) => {
$dollarcall! {$dollar($dollarprefix)*
fields = {$(
$(#[$field_attr])*
$field_vis $field_name: $field_type,
)*},
cx = $dollarcx,
}
};
}
#[allow(unused_imports)]
pub(crate) use [<__private_field_inspect_for $name>] as $name;
}
};
}
#[cfg(test)]
mod test {
use serde::{Deserialize, Serialize};
#[test]
fn flatten_structs() {
flatten_structs!(
#[allow(unused)]
pub struct BaseStruct {
enable: bool,
#[flatten]
n1: NestedStruct1,
#[flatten]
n2: NestedStruct2,
}
);
flatten_structs!(
#[allow(unused)]
struct NestedStruct1 {
value: f32,
}
);
flatten_structs!(
#[allow(unused)]
struct NestedStruct2 {
goal: f32,
#[flatten]
sn: SubNestedStruct,
}
);
flatten_structs!(
#[allow(unused)]
struct SubNestedStruct {
min: f32,
max: f32,
}
);
let base_struct = BaseStruct {
enable: true,
value: 0.0,
goal: 10.0,
min: 0.0,
max: 20.0,
};
assert!(base_struct.enable);
}
#[test]
fn flatten_serde_struct() {
flatten_structs!(
#[derive(Serialize, Deserialize)]
pub struct BaseSerdeStruct {
enable: bool,
#[flatten]
nested: NestedSerdeStruct,
}
);
flatten_structs!(
#[allow(unused)]
#[derive(Serialize, Deserialize)]
struct NestedSerdeStruct {
min: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
max: Option<f32>,
}
);
let base_struct = BaseSerdeStruct {
enable: false,
min: Some(0.0),
max: None,
};
let base_struct_json = serde_json::to_string_pretty(&base_struct).unwrap();
pretty_assertions::assert_eq!(
r#"{
"enable": false,
"min": 0.0
}"#,
base_struct_json
);
}
}