mirror of
https://github.com/danbulant/cushy
synced 2026-06-21 15:42:07 +00:00
114 lines
3.8 KiB
Rust
114 lines
3.8 KiB
Rust
use manyhow::{bail, ensure};
|
|
use quote::ToTokens;
|
|
use syn::{Data, DeriveInput, Field, Variant};
|
|
|
|
use crate::*;
|
|
|
|
pub fn linear_interpolate(
|
|
DeriveInput {
|
|
ident: item_ident,
|
|
generics,
|
|
data,
|
|
..
|
|
}: DeriveInput,
|
|
) -> Result<TokenStream> {
|
|
if let Some(generic) = generics.type_params().next() {
|
|
bail!(generic, "generics not supported");
|
|
}
|
|
|
|
let doc;
|
|
|
|
let body = match data {
|
|
Data::Struct(data) => {
|
|
let fields = match data.fields {
|
|
syn::Fields::Unit => bail!(item_ident, "unit structs are not supported"),
|
|
fields => fields
|
|
.into_iter()
|
|
.enumerate()
|
|
.map(|(idx, Field { ident, .. })| {
|
|
let ident = ident
|
|
.map(ToTokens::into_token_stream)
|
|
.unwrap_or_else(|| proc_macro2::Literal::usize_unsuffixed(idx).into_token_stream());
|
|
quote!(#ident: ::cushy::animation::LinearInterpolate::lerp(&self.#ident, &__target.#ident, __percent),)
|
|
}),
|
|
};
|
|
doc = "# Panics\n Panics if any field's lerp panics (this should only happen on percentages outside 0..1 range).";
|
|
quote!(#item_ident{#(#fields)*})
|
|
}
|
|
Data::Enum(data) => {
|
|
let variants = data
|
|
.variants
|
|
.into_iter()
|
|
.map(
|
|
|Variant {
|
|
ident,
|
|
fields,
|
|
discriminant,
|
|
..
|
|
}| {
|
|
if let Some(discriminant) = discriminant {
|
|
bail!(discriminant, "discriminants are not supported");
|
|
}
|
|
ensure!(fields.is_empty(), fields, "enum fields are not supported");
|
|
Ok(quote!(#item_ident::#ident #fields))
|
|
},
|
|
)
|
|
.collect::<Result<Vec<_>>>()?;
|
|
let last = variants
|
|
.last()
|
|
.map(ToTokens::to_token_stream)
|
|
.unwrap_or_else(|| quote!(unreachable!()));
|
|
|
|
let idx: Vec<_> = (0..variants.len()).collect();
|
|
doc = "# Panics\n Panics if the the enum variants are overflown (this can only happen on percentages outside 0..1 range).";
|
|
quote! {
|
|
# use ::cushy::animation::LinearInterpolate;
|
|
fn variant_to_index(__v: &#item_ident) -> usize {
|
|
match __v {
|
|
#(#variants => #idx,)*
|
|
}
|
|
}
|
|
let __self = variant_to_index(&self);
|
|
let __target = variant_to_index(&__target);
|
|
match LinearInterpolate::lerp(&__self, &__target, __percent) {
|
|
#(#idx => #variants,)*
|
|
_ => #last,
|
|
}
|
|
}
|
|
}
|
|
Data::Union(union) => bail!((union.union_token, union.fields), "unions not supported"),
|
|
};
|
|
|
|
Ok(quote! {
|
|
impl ::cushy::animation::LinearInterpolate for #item_ident {
|
|
#[doc = #doc]
|
|
fn lerp(&self, __target: &Self, __percent: f32) -> Self {
|
|
#body
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
expansion_snapshot! {struct_
|
|
#[derive(linear_interpolate)]
|
|
struct HelloWorld {
|
|
fielda: Hello,
|
|
fieldb: World,
|
|
}
|
|
}
|
|
expansion_snapshot! {tuple_struct
|
|
#[derive(linear_interpolate)]
|
|
struct HelloWorld(Hello, World);
|
|
}
|
|
expansion_snapshot! {enum_
|
|
#[derive(linear_interpolate)]
|
|
enum Enum{A, B}
|
|
}
|
|
expansion_snapshot! {empty_enum
|
|
#[derive(linear_interpolate)]
|
|
enum Enum{}
|
|
}
|
|
}
|