use quote::quote; use syn::{Path, Ident}; use proc_macro2::Span; #[derive(Default)] pub(crate) struct VecWrapper; #[allow(dead_code)] impl VecWrapper { fn vec_size(&self, wrapper_ident: &Ident) -> usize { let name = wrapper_ident.to_string(); name[name.len() - 1..].parse::() .or_else(|_| name[name.len() - 2.. name.len() - 1].parse::()) .expect("Failure to grab Vec size from ident name") } /// Returns the token stream of the type of the axis of the vec (Vec2 vs IVec2 vs I64Vec2, etc.) fn vec_axis_type(&self, wrapper_ident: &Ident) -> &'static str { let name = wrapper_ident.to_string(); let start = name.find("Vec").unwrap(); let before = &name[start - 1.. start]; match before { "D" => return "f64", "I" => return "i32", "U" => return "u32", "B" => return "bool", _ => {}, } //println!("before is {before}"); let three_before = &name[start - 3.. start]; match three_before { "I64" => return "i64", "U64" => return "u64", _ => {}, } //println!("three before is {three_before}"); "f32" } pub fn to_field_tokens(&self, wrapped_path: &Path, wrapper_ident: &Ident) -> proc_macro2::TokenStream { let mut consts = vec![quote!(ZERO), quote!(ONE), quote!(X), quote!(Y), ]; // , quote!(AXES) let vec_size = self.vec_size(wrapper_ident); let axis_type_name = self.vec_axis_type(wrapper_ident); if axis_type_name.contains("b") { return quote! { builder.field("FALSE", #wrapper_ident(#wrapped_path::FALSE)); builder.field("TRUE", #wrapper_ident(#wrapped_path::TRUE)); }; } if vec_size >= 3 { consts.push(quote!(Z)); // no negative numbers for unsigned vecs if !axis_type_name.contains("u") { consts.push(quote!(NEG_Z)); } } if vec_size == 4 { consts.push(quote!(W)); // no negative numbers for unsigned vecs if !axis_type_name.contains("u") { consts.push(quote!(NEG_W)); } } // no negative numbers for unsigned vecs if !axis_type_name.contains("u") { consts.push(quote!(NEG_X)); consts.push(quote!(NEG_Y)); consts.push(quote!(NEG_ONE)); } if axis_type_name.contains("f") { consts.push(quote!(NAN)) } let const_tokens = consts.iter().map(|cnst| { let const_name = cnst.to_string(); quote! { builder.field(#const_name, #wrapper_ident(#wrapped_path::#cnst)); } }); quote! { #(#const_tokens)* } } pub fn to_method_tokens(&self, wrapper_ident: &Ident) -> proc_macro2::TokenStream { let vec_size = self.vec_size(wrapper_ident); let axis_type_name = self.vec_axis_type(wrapper_ident); // methods that only some vecs have let mut optional_methods = vec![]; // boolean vectors dont have much :( if axis_type_name.contains("b") { return quote!(); // TODO: all, any, bitmask, splat } if axis_type_name.contains("f") { let type_id = Ident::new(axis_type_name, Span::call_site()); optional_methods.push( quote! { builder.method("clamp_length", |_, this, (min, max): (#type_id, #type_id)| { Ok(#wrapper_ident(this.clamp_length(min, max))) }); builder.method("abs_diff_eq", |_, this, (rhs, max_abs_diff): (#wrapper_ident, #type_id)| { Ok(this.abs_diff_eq(rhs.0, max_abs_diff)) }); builder.method("ceil", |_, this, (): ()| { Ok(#wrapper_ident(this.ceil())) }); } ); if vec_size != 4 { optional_methods.push( quote! { builder.method("angle_between", |_, this, (rhs,): (#wrapper_ident,)| { Ok(this.angle_between(rhs.0)) }); } ) } } if !axis_type_name.contains("u") { optional_methods.push( quote! { builder.method("abs", |_, this, (): ()| { Ok(#wrapper_ident(this.abs())) }); } ) } let optional_methods_iter = optional_methods.iter(); quote! { builder.method("clamp", |_, this, (min, max): (#wrapper_ident, #wrapper_ident)| { Ok(#wrapper_ident(this.clamp(min.0, max.0))) }); // TODO: Not all Vecs have this /* builder.method("clamp_length", |_, this, (min, max): (f32, f32)| { Ok(#wrapper_ident(this.clamp_length(min, max))) }); */ builder.method("to_array", |_, this, (): ()| { Ok(this.to_array()) }); #(#optional_methods_iter)* } } }