#[macro_export]
macro_rules! mod_set {
(@munch $set:ident,) => {};
(@munch $set:ident, @complex $name:literal => [ $( ($part:literal, $reduce:expr) ),* $(,)? ] => $expr:literal , $($rest:tt)*) => {
$set.add_builder($crate::modifier_set::ComplexAttribute::new(
$name, &[ $( ($part, $reduce) ),* ], $expr,
));
$crate::mod_set!(@munch $set, $($rest)*);
};
(@munch $set:ident, @complex $name:literal => [ $( ($part:literal, $reduce:expr) ),* $(,)? ] => $expr:literal) => {
$set.add_builder($crate::modifier_set::ComplexAttribute::new(
$name, &[ $( ($part, $reduce) ),* ], $expr,
));
};
(@munch $set:ident, @build $builder:expr , $($rest:tt)*) => {
$set.add_builder($builder);
$crate::mod_set!(@munch $set, $($rest)*);
};
(@munch $set:ident, @build $builder:expr) => {
$set.add_builder($builder);
};
(@munch $set:ident, $attribute:literal [ $($tag:tt)+ ] => $value:expr , $($rest:tt)*) => {
$set.add_tagged($attribute, $value, $($tag)+);
$crate::mod_set!(@munch $set, $($rest)*);
};
(@munch $set:ident, $attribute:literal [ $($tag:tt)+ ] => $value:expr) => {
$set.add_tagged($attribute, $value, $($tag)+);
};
(@munch $set:ident, $attribute:literal => $value:expr , $($rest:tt)*) => {
$set.add($attribute, $value);
$crate::mod_set!(@munch $set, $($rest)*);
};
(@munch $set:ident, $attribute:literal => $value:expr) => {
$set.add($attribute, $value);
};
(@munch $set:ident, $attribute:path [ $($tag:tt)+ ] => $value:expr , $($rest:tt)*) => {
$set.add_tagged($attribute, $value, $($tag)+);
$crate::mod_set!(@munch $set, $($rest)*);
};
(@munch $set:ident, $attribute:path [ $($tag:tt)+ ] => $value:expr) => {
$set.add_tagged($attribute, $value, $($tag)+);
};
(@munch $set:ident, $attribute:path => $value:expr , $($rest:tt)*) => {
$set.add($attribute, $value);
$crate::mod_set!(@munch $set, $($rest)*);
};
(@munch $set:ident, $attribute:path => $value:expr) => {
$set.add($attribute, $value);
};
{ $($tt:tt)* } => {{
let mut _set = $crate::modifier_set::ModifierSet::new();
$crate::mod_set!(@munch _set, $($tt)*);
_set
}};
}
#[macro_export]
macro_rules! attributes {
{ $($tt:tt)* } => {
$crate::modifier_set::AttributeInitializer::new($crate::mod_set!{ $($tt)* })
};
}
#[cfg(test)]
mod tests {
use crate::modifier_set::ModifierValue;
use crate::tags::TagMask;
const STRENGTH: &str = "Strength";
const DAMAGE_ADDED: &str = "Damage.Added";
fn literal(value: &ModifierValue) -> f32 {
match value {
ModifierValue::Literal(v) => *v,
other => panic!("expected literal, got {other:?}"),
}
}
fn expr(value: &ModifierValue) -> &str {
match value {
ModifierValue::ExprSource(s) => s,
other => panic!("expected expr, got {other:?}"),
}
}
#[test]
fn accepts_literal_and_path_names() {
const FIRE: TagMask = TagMask::bit(0);
let set = mod_set! {
"Vitality" => 10.0,
STRENGTH => 5.0,
STRENGTH => "Vitality * 2.0",
DAMAGE_ADDED [FIRE] => 7.0,
};
let entries = set.entries();
assert_eq!(entries.len(), 4);
assert_eq!(entries[0].attribute, "Vitality");
assert_eq!(literal(&entries[0].value), 10.0);
assert_eq!(entries[1].attribute, "Strength");
assert_eq!(literal(&entries[1].value), 5.0);
assert_eq!(entries[2].attribute, "Strength");
assert_eq!(expr(&entries[2].value), "Vitality * 2.0");
assert_eq!(entries[3].attribute, "Damage.Added");
assert_eq!(literal(&entries[3].value), 7.0);
assert_eq!(entries[3].tag, FIRE);
}
}