use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, Fields};
fn has_animation_attr(field: &syn::Field) -> bool {
field
.attrs
.iter()
.any(|attr| attr.path().is_ident("animation"))
}
#[proc_macro_derive(BlincComponent, attributes(animation))]
pub fn derive_blinc_component(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let field_methods = match &input.data {
Data::Struct(data) => match &data.fields {
Fields::Named(fields) => {
fields
.named
.iter()
.map(|field| {
let field_name = field.ident.as_ref().unwrap();
let field_type = &field.ty;
let method_name =
syn::Ident::new(&format!("use_{}", field_name), field_name.span());
let field_key = format!("{}", field_name);
let method_name_for =
syn::Ident::new(&format!("use_{}_for", field_name), field_name.span());
let method_name_auto =
syn::Ident::new(&format!("use_{}_auto", field_name), field_name.span());
if has_animation_attr(field) {
quote! {
pub fn #method_name<C: blinc_animation::AnimationContext>(
ctx: &C,
initial: f32,
config: blinc_animation::SpringConfig,
) -> blinc_animation::SharedAnimatedValue {
let key = format!("{}:{}", Self::COMPONENT_KEY, #field_key);
ctx.use_animated_value_for(key, initial, config)
}
pub fn #method_name_for<C: blinc_animation::AnimationContext, K: std::fmt::Display>(
ctx: &C,
instance_key: K,
initial: f32,
config: blinc_animation::SpringConfig,
) -> blinc_animation::SharedAnimatedValue {
let key = format!("{}:{}:{}", Self::COMPONENT_KEY, #field_key, instance_key);
ctx.use_animated_value_for(key, initial, config)
}
#[track_caller]
pub fn #method_name_auto<C: blinc_animation::AnimationContext>(
ctx: &C,
initial: f32,
config: blinc_animation::SpringConfig,
) -> blinc_animation::SharedAnimatedValue {
let loc = std::panic::Location::caller();
let key = format!("{}:{}:{}:{}:{}",
Self::COMPONENT_KEY, #field_key,
loc.file(), loc.line(), loc.column());
ctx.use_animated_value_for(key, initial, config)
}
}
} else {
quote! {
pub fn #method_name<C: blinc_core::BlincContext>(
ctx: &C,
initial: #field_type,
) -> blinc_core::State<#field_type>
where
#field_type: Clone + Send + 'static,
{
let key = format!("{}:{}", Self::COMPONENT_KEY, #field_key);
ctx.use_state_keyed(&key, || initial)
}
pub fn #method_name_for<C: blinc_core::BlincContext, K: std::fmt::Display>(
ctx: &C,
instance_key: K,
initial: #field_type,
) -> blinc_core::State<#field_type>
where
#field_type: Clone + Send + 'static,
{
let key = format!("{}:{}:{}", Self::COMPONENT_KEY, #field_key, instance_key);
ctx.use_state_keyed(&key, || initial)
}
#[track_caller]
pub fn #method_name_auto<C: blinc_core::BlincContext>(
ctx: &C,
initial: #field_type,
) -> blinc_core::State<#field_type>
where
#field_type: Clone + Send + 'static,
{
let loc = std::panic::Location::caller();
let key = format!("{}:{}:{}:{}:{}",
Self::COMPONENT_KEY, #field_key,
loc.file(), loc.line(), loc.column());
ctx.use_state_keyed(&key, || initial)
}
}
}
})
.collect::<Vec<_>>()
}
Fields::Unnamed(_) => Vec::new(),
Fields::Unit => Vec::new(),
},
_ => Vec::new(),
};
let expanded = quote! {
impl #name {
pub const COMPONENT_KEY: &'static str = concat!(module_path!(), "::", stringify!(#name));
#[track_caller]
pub fn instance_key() -> String {
let loc = std::panic::Location::caller();
format!("{}:{}:{}:{}",
Self::COMPONENT_KEY,
loc.file(), loc.line(), loc.column())
}
#[track_caller]
pub fn instance_key_for<K: std::fmt::Display>(suffix: K) -> String {
let loc = std::panic::Location::caller();
format!("{}:{}:{}:{}:{}",
Self::COMPONENT_KEY,
loc.file(), loc.line(), loc.column(),
suffix)
}
pub fn use_animated_value<C: blinc_animation::AnimationContext>(
ctx: &C,
initial: f32,
config: blinc_animation::SpringConfig,
) -> blinc_animation::SharedAnimatedValue {
ctx.use_animated_value_for(Self::COMPONENT_KEY, initial, config)
}
pub fn use_animated_value_with<C: blinc_animation::AnimationContext>(
ctx: &C,
suffix: &str,
initial: f32,
config: blinc_animation::SpringConfig,
) -> blinc_animation::SharedAnimatedValue {
let key = format!("{}:{}", Self::COMPONENT_KEY, suffix);
ctx.use_animated_value_for(key, initial, config)
}
pub fn use_animated_timeline<C: blinc_animation::AnimationContext>(
ctx: &C,
) -> blinc_animation::SharedAnimatedTimeline {
ctx.use_animated_timeline_for(Self::COMPONENT_KEY)
}
pub fn use_animated_timeline_with<C: blinc_animation::AnimationContext>(
ctx: &C,
suffix: &str,
) -> blinc_animation::SharedAnimatedTimeline {
let key = format!("{}:{}", Self::COMPONENT_KEY, suffix);
ctx.use_animated_timeline_for(key)
}
#(#field_methods)*
}
};
TokenStream::from(expanded)
}