use core::iter::{self, Repeat, Take};
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use quote::{format_ident, quote};
use syn::parse_macro_input;
use syn::spanned::Spanned;
use syn::{Error, Fields, Generics, Index, ItemStruct, Path, Type, WhereClause};
macro_rules! err {
($span:expr, $fmt:literal $(,)? $($arg:expr),*) => {{
TokenStream::from(
Error::new($span, format!($fmt, $($arg),*)).into_compile_error()
)
}}
}
#[proc_macro_attribute]
pub fn vector(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as syn::AttributeArgs);
if let Some(arg) = args.get(0) {
return err!(arg.span(), "found unexpected argument in macro attribute");
}
let item = parse_macro_input!(input as ItemStruct);
let output = expand(item);
match output {
Ok(tokens) => tokens,
Err(err) => err,
}
}
fn check_primitive(path: &Option<Path>) -> (bool, bool, bool) {
let path = path.as_ref().expect("type path should always be some path");
if let Some(seg) = path.segments.last() {
const PRIMITIVE: [&str; 12] = [
"f32", "f64", "i8", "i16", "i32", "i64", "i128", "u8", "u16", "u32", "u64", "u128",
];
let mut ret = (false, false, false);
for (i, x) in PRIMITIVE.iter().enumerate() {
if seg.ident == x {
ret.0 = true;
if i < 2 {
ret.2 = true;
}
if i < 7 {
ret.1 = true;
}
}
}
return ret;
}
(false, false, false)
}
fn repeat<T>(x: T, n: usize) -> Take<Repeat<T>>
where
T: Clone,
{
iter::repeat(x).take(n)
}
fn expand(item: ItemStruct) -> std::result::Result<TokenStream, TokenStream> {
let parse_result = parse_struct(&item);
if let Some(err) = parse_result.compile_error {
return Err(err);
}
let ParseResult {
ident,
generics,
generic_ident: _,
where_clause,
fields,
fields_count,
type_path,
is_tuple,
tuple_indexes,
is_generic,
is_primitive,
is_signed_primitive,
is_float,
..
} = parse_result;
let where_prelude = if where_clause.is_some() {
quote!(#where_clause)
} else {
quote!(where)
};
let item_repr_c = {
let mut item_repr_c = item;
item_repr_c.attrs.push(syn::parse_quote!(#[repr(C)]));
item_repr_c
.attrs
.push(syn::parse_quote!(#[derive(Clone, Copy, PartialEq)]));
quote!(#item_repr_c)
};
let impl_as_ref = {
quote!(
impl #generics core::convert::AsRef<[#type_path]> for #ident #generics #where_clause {
#[inline]
fn as_ref(&self) -> &[#type_path] {
self.as_slice()
}
}
impl #generics core::convert::AsRef<Self> for #ident #generics #where_clause {
#[inline]
fn as_ref(&self) -> &Self {
self
}
}
impl #generics core::convert::AsMut<[#type_path]> for #ident #generics #where_clause {
#[inline]
fn as_mut(&mut self) -> &mut [#type_path] {
self.as_mut_slice()
}
}
impl #generics core::convert::AsMut<Self> for #ident #generics #where_clause {
#[inline]
fn as_mut(&mut self) -> &mut Self {
self
}
}
)
};
let impl_de_ref = {
quote!(
impl #generics core::ops::Deref for #ident #generics #where_clause {
type Target = [#type_path];
#[inline]
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl #generics core::ops::DerefMut for #ident #generics #where_clause {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut_slice()
}
}
)
};
let impl_borrow = {
quote!(
impl #generics core::borrow::Borrow<[#type_path]> for #ident #generics #where_clause {
#[inline]
fn borrow(&self) -> &[#type_path] {
self.as_slice()
}
}
impl #generics core::borrow::BorrowMut<[#type_path]> for #ident #generics #where_clause {
#[inline]
fn borrow_mut(&mut self) -> &mut [#type_path] {
self.as_mut_slice()
}
}
)
};
let impl_from_inner = {
let where_clause = if is_generic {
quote!(#where_prelude #type_path: Copy)
} else {
quote!(#where_clause)
};
let var = Ident::new("inner", Span::call_site());
let ts = std::iter::repeat(var.clone()).take(fields_count);
quote!(
impl #generics core::convert::From<#type_path> for #ident #generics #where_clause {
#[inline]
fn from(#var: #type_path) -> Self {
#ident::new(#(#ts),*)
}
}
)
};
let impl_from_tuple_array = {
let ts = repeat(&type_path, fields_count);
let tuple = quote!((#(#ts),*));
let array = quote!([#type_path; #fields_count]);
quote!(
impl #generics core::convert::From<#tuple> for #ident #generics #where_clause {
#[inline]
fn from(tuple: #tuple) -> Self {
let (#(#fields),*) = tuple;
#ident::new(#(#fields),*)
}
}
impl #generics core::convert::From<#array> for #ident #generics #where_clause {
#[inline]
fn from(array: #array) -> Self {
let [#(#fields),*] = array;
#ident::new(#(#fields),*)
}
}
)
};
let impl_into_tuple_array = {
let ts = repeat(&type_path, fields_count);
let tuple = quote!((#(#ts),*));
let array = quote!([#type_path; #fields_count]);
let inner_bracket = if is_tuple {
quote!(#(self.#tuple_indexes),*)
} else {
quote!(#(self.#fields),*)
};
quote!(
#[allow(clippy::from_over_into)]
impl #generics core::convert::Into<#tuple> for #ident #generics #where_clause {
#[inline]
fn into(self) -> #tuple {
(#inner_bracket)
}
}
#[allow(clippy::from_over_into)]
impl #generics core::convert::Into<#array> for #ident #generics #where_clause {
#[inline]
fn into(self) -> #array {
[#inner_bracket]
}
}
)
};
let impl_unop = {
let impl_unop = |op_trait: proc_macro2::TokenStream,
op: proc_macro2::TokenStream|
-> proc_macro2::TokenStream {
let inner_new = if is_tuple {
quote!(#(self.#tuple_indexes.#op()),*)
} else {
quote!(#(self.#fields.#op()),*)
};
let where_clause = if is_generic {
quote!(#where_prelude #type_path: core::ops::#op_trait<Output=#type_path>)
} else {
quote!(#where_clause)
};
if is_generic || is_signed_primitive {
quote!(
impl #generics core::ops::#op_trait for #ident #generics #where_clause {
type Output = Self;
#[inline]
fn #op(self) -> Self::Output {
Self::new(#inner_new)
}
}
)
} else {
quote!()
}
};
let impl_neg = impl_unop(quote!(Neg), quote!(neg));
quote!(#impl_neg)
};
let impl_binop = {
let impl_binop = |op_trait: proc_macro2::TokenStream,
op: proc_macro2::TokenStream,
op_assgin: bool|
-> proc_macro2::TokenStream {
let output = if !op_assgin {
quote!(, Output=#type_path)
} else {
quote!()
};
let where_clause = match is_generic {
true => {
quote!(
#where_prelude
#type_path: core::ops::#op_trait<#type_path #output>
)
}
false => {
quote!(
#where_prelude
)
}
};
let inner = match (is_tuple, op_assgin) {
(true, false) => {
quote!(Self::new(#(self.#tuple_indexes.#op(rhs.#tuple_indexes),)*))
}
(false, false) => quote!(Self::new(#(self.#fields.#op(rhs.#fields),)*)),
(true, true) => quote!(#(self.#tuple_indexes.#op(rhs.#tuple_indexes);)*),
(false, true) => quote!(#(self.#fields.#op(rhs.#fields);)*),
};
if !op_assgin {
quote!(
impl #generics core::ops::#op_trait<Self> for #ident #generics #where_clause
{
type Output = Self;
#[inline]
fn #op(self, rhs: Self) -> Self::Output {
#inner
}
}
)
} else {
quote!(
impl #generics core::ops::#op_trait<Self> for #ident #generics #where_clause
{
#[inline]
fn #op(&mut self, rhs: Self) {
#inner
}
}
)
}
};
let impl_add = impl_binop(quote!(Add), quote!(add), false);
let impl_sub = impl_binop(quote!(Sub), quote!(sub), false);
let impl_mul = impl_binop(quote!(Mul), quote!(mul), false);
let impl_div = impl_binop(quote!(Div), quote!(div), false);
let impl_add_assign = impl_binop(quote!(AddAssign), quote!(add_assign), true);
let impl_sub_assign = impl_binop(quote!(SubAssign), quote!(sub_assign), true);
let impl_mul_assign = impl_binop(quote!(MulAssign), quote!(mul_assign), true);
let impl_div_assign = impl_binop(quote!(DivAssign), quote!(div_assign), true);
quote! {
#impl_add
#impl_sub
#impl_mul
#impl_div
#impl_add_assign
#impl_sub_assign
#impl_mul_assign
#impl_div_assign
}
};
let impl_binop_inner = {
let impl_mul = {
let where_clause = if is_generic {
quote!(#where_prelude #type_path: core::marker::Copy + core::ops::Mul<Output=T>)
} else {
quote!()
};
let inner = if is_tuple {
quote!(Self::new(#(self.#tuple_indexes * rhs),*))
} else {
quote!(Self::new(#(self.#fields * rhs),*))
};
quote!(
impl #generics core::ops::Mul<#type_path> for #ident #generics #where_clause
{
type Output = Self;
#[inline]
fn mul(self, rhs: #type_path) -> Self::Output {
#inner
}
}
)
};
let impl_mul_assign = {
let where_clause = if is_generic {
quote!(#where_prelude #type_path: core::marker::Copy + core::ops::MulAssign)
} else {
quote!()
};
let inner = if is_tuple {
quote!(#(self.#tuple_indexes *= rhs;)*)
} else {
quote!(#(self.#fields *= rhs;)*)
};
quote!(
impl #generics core::ops::MulAssign<#type_path> for #ident #generics #where_clause
{
#[inline]
fn mul_assign(&mut self, rhs: #type_path) {
#inner
}
}
)
};
let impl_div = {
let where_clause = if is_generic {
quote!(
#where_prelude #type_path: core::marker::Copy
+ core::ops::Mul<Output=T>
+ core::ops::Div<Output=T>
+ num_traits::identities::One
)
} else {
quote!()
};
let one = if is_generic {
quote!(#type_path::one())
} else if is_float {
quote!(1.0)
} else {
quote!(1)
};
let inner = if is_tuple {
quote!(Self::new(#(self.#tuple_indexes * inv),*))
} else {
quote!(Self::new(#(self.#fields * inv),*))
};
quote!(
impl #generics core::ops::Div<#type_path> for #ident #generics #where_clause
{
type Output = Self;
#[inline]
fn div(self, rhs: #type_path) -> Self::Output {
let inv = #one / rhs;
#inner
}
}
)
};
let impl_div_assign = {
let where_clause = if is_generic {
quote!(
#where_prelude #type_path: core::marker::Copy
+ core::ops::Div<Output=T>
+ core::ops::Mul<Output=T>
+ core::ops::MulAssign
+ num_traits::identities::One
)
} else {
quote!()
};
let one = if is_generic {
quote!(#type_path::one())
} else if is_float {
quote!(1.)
} else {
quote!(1)
};
let inner = if is_tuple {
quote!(#(self.#tuple_indexes *= inv;)*)
} else {
quote!(#(self.#fields *= inv;)*)
};
quote!(
impl #generics core::ops::DivAssign<#type_path> for #ident #generics #where_clause
{
#[inline]
fn div_assign(&mut self, rhs: #type_path) {
let inv = #one / rhs;
#inner
}
}
)
};
quote!(
#impl_mul
#impl_mul_assign
#impl_div
#impl_div_assign
)
};
#[rustfmt::skip]
let impl_binop_commutative = {
let impl_binop_commutative = |op_trait: proc_macro2::TokenStream,
op: proc_macro2::TokenStream,
primitive: Option<proc_macro2::TokenStream>|
-> proc_macro2::TokenStream {
let ident = if is_generic {
quote!(#ident<#primitive>)
} else {
quote!(#ident)
};
let primitive = if is_generic { quote!(#primitive) } else { quote!(#type_path) };
quote!(
impl core::ops::#op_trait<#ident> for #primitive {
type Output = #ident;
#[inline]
fn #op(self, rhs: #ident) -> Self::Output {
let lhs: #ident = self.into();
rhs.#op(lhs)
}
}
)
};
if is_generic {
let impl_add_f32 = impl_binop_commutative(quote!(Add), quote!(add), Some(quote!(f32)));
let impl_add_f64 = impl_binop_commutative(quote!(Add), quote!(add), Some(quote!(f64)));
let impl_add_i8 = impl_binop_commutative(quote!(Add), quote!(add), Some(quote!(i8)));
let impl_add_u8 = impl_binop_commutative(quote!(Add), quote!(add), Some(quote!(u8)));
let impl_add_i16 = impl_binop_commutative(quote!(Add), quote!(add), Some(quote!(i16)));
let impl_add_u16 = impl_binop_commutative(quote!(Add), quote!(add), Some(quote!(u16)));
let impl_add_i32 = impl_binop_commutative(quote!(Add), quote!(add), Some(quote!(i32)));
let impl_add_u32 = impl_binop_commutative(quote!(Add), quote!(add), Some(quote!(u32)));
let impl_add_i64 = impl_binop_commutative(quote!(Add), quote!(add), Some(quote!(i64)));
let impl_add_u64 = impl_binop_commutative(quote!(Add), quote!(add), Some(quote!(u64)));
let impl_add_i128 = impl_binop_commutative(quote!(Add), quote!(add), Some(quote!(i128)));
let impl_add_u128 = impl_binop_commutative(quote!(Add), quote!(add), Some(quote!(u128)));
let impl_mul_f32 = impl_binop_commutative(quote!(Mul), quote!(mul), Some(quote!(f32)));
let impl_mul_f64 = impl_binop_commutative(quote!(Mul), quote!(mul), Some(quote!(f64)));
let impl_mul_i8 = impl_binop_commutative(quote!(Mul), quote!(mul), Some(quote!(i8)));
let impl_mul_u8 = impl_binop_commutative(quote!(Mul), quote!(mul), Some(quote!(u8)));
let impl_mul_i16 = impl_binop_commutative(quote!(Mul), quote!(mul), Some(quote!(i16)));
let impl_mul_u16 = impl_binop_commutative(quote!(Mul), quote!(mul), Some(quote!(u16)));
let impl_mul_i32 = impl_binop_commutative(quote!(Mul), quote!(mul), Some(quote!(i32)));
let impl_mul_u32 = impl_binop_commutative(quote!(Mul), quote!(mul), Some(quote!(u32)));
let impl_mul_i64 = impl_binop_commutative(quote!(Mul), quote!(mul), Some(quote!(i64)));
let impl_mul_u64 = impl_binop_commutative(quote!(Mul), quote!(mul), Some(quote!(u64)));
let impl_mul_i128 = impl_binop_commutative(quote!(Mul), quote!(mul), Some(quote!(i128)));
let impl_mul_u128 = impl_binop_commutative(quote!(Mul), quote!(mul), Some(quote!(u128)));
quote!(
#impl_add_f32
#impl_add_f64
#impl_add_i8
#impl_add_u8
#impl_add_i16
#impl_add_u16
#impl_add_i32
#impl_add_u32
#impl_add_i64
#impl_add_u64
#impl_add_i128
#impl_add_u128
#impl_mul_f32
#impl_mul_f64
#impl_mul_i8
#impl_mul_u8
#impl_mul_i16
#impl_mul_u16
#impl_mul_i32
#impl_mul_u32
#impl_mul_i64
#impl_mul_u64
#impl_mul_i128
#impl_mul_u128
)
} else if is_primitive {
let impl_add = impl_binop_commutative(quote!(Add), quote!(add), None);
let impl_mul = impl_binop_commutative(quote!(Mul), quote!(mul), None);
quote!(
#impl_add
#impl_mul
)
} else {
quote!()
}
};
let impl_fn_shared = {
let impl_consts = {
let const_zero = if is_primitive {
let zero = if is_float { quote!(0.) } else { quote!(0) };
let zeros = repeat(&zero, fields_count);
let bracketed = if is_tuple {
quote!((#(#zeros),*))
} else {
quote!({#(#fields: #zero),*})
};
quote!(
pub const ZERO: #ident = #ident #bracketed;
)
} else {
quote!()
};
let const_one = if is_primitive {
let one = if is_float { quote!(1.) } else { quote!(1) };
let ones = repeat(&one, fields_count);
let bracketed = if is_tuple {
quote!((#(#ones),*))
} else {
quote!({#(#fields: #one),*})
};
quote!(
pub const ONE: #ident = #ident #bracketed;
)
} else {
quote!()
};
let const_lanes = {
quote!(
pub const LANES: usize = #fields_count;
)
};
quote!(
#const_lanes
#const_zero
#const_one
)
};
let impl_fn_new = {
let body = if is_tuple {
quote!(#ident(#(#fields),*))
} else {
quote!(#ident{#(#fields),*})
};
quote!(
#[inline]
pub fn new(#(#fields: #type_path),*) -> Self {
#body
}
)
};
let impl_fn_splat = {
let where_clause = if is_generic {
quote!(where #type_path: core::marker::Copy)
} else {
quote!()
};
quote!(
#[inline]
pub fn splat(value: #type_path) -> Self #where_clause {
Self::from(value)
}
)
};
let impl_fn_as_ptr = {
quote!(
#[inline]
fn as_ptr(&self) -> *const #type_path {
self as *const _ as *const #type_path
}
#[inline]
fn as_mut_ptr(&mut self) -> *mut #type_path {
self as *mut _ as *mut #type_path
}
)
};
let impl_fn_as_slice = {
quote!(
#[inline]
pub fn as_slice(&self) -> &[#type_path] {
unsafe {
core::slice::from_raw_parts(self.as_ptr(), #fields_count)
}
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [#type_path] {
unsafe {
core::slice::from_raw_parts_mut(self.as_mut_ptr(), #fields_count)
}
}
)
};
let impl_fn_unit_field = if fields_count <= 4 {
let fn_names = if is_tuple {
tuple_indexes
.clone()
.into_iter()
.map(|x| Ident::new(&format!("unit_{}", x.index), x.span()))
.collect::<Vec<Ident>>()
} else {
fields
.clone()
.into_iter()
.map(|x| Ident::new(&format!("unit_{}", x), x.span()))
.collect::<Vec<Ident>>()
};
let where_clause = if is_generic {
quote!( where #type_path: core::marker::Copy + num_traits::One + num_traits::Zero)
} else {
quote!()
};
let (zero, one) = match (is_generic, is_float) {
(true, _) => (quote!(#type_path::zero()), quote!(#type_path::one())),
(false, true) => (quote!(0.0), quote!(1.0)),
(false, false) => (quote!(0), quote!(1)),
};
let docs = if is_tuple {
tuple_indexes
.clone()
.into_iter()
.map(|x| {
format!(
"Returns a unit vector which points along the `{}` axis.",
x.index
)
})
.collect::<Vec<String>>()
} else {
fields
.clone()
.into_iter()
.map(|x| format!("Returns a unit vector which points along the `{}` axis.", x))
.collect::<Vec<String>>()
};
if is_tuple {
quote!(
#(
#[doc = #docs]
#[inline]
pub fn #fn_names() -> #ident #generics #where_clause {
let mut unit_field = #ident::splat(#zero);
unit_field.#tuple_indexes = #one;
unit_field
}
)*
)
} else {
quote!(
#(
#[doc = #docs]
pub fn #fn_names() -> #ident #generics #where_clause {
let mut unit_field = #ident::splat(#zero);
unit_field.#fields = #one;
unit_field
}
)*
)
}
} else {
quote!()
};
let impl_fn_sum = {
let where_clause = if is_generic {
quote!(where #type_path: core::ops::Add<Output=#type_path>)
} else {
quote!()
};
let sum = if is_tuple {
quote!(#(self.#tuple_indexes)+*)
} else {
quote!(#(self.#fields)+*)
};
quote!(
#[inline]
pub fn sum(self) -> #type_path #where_clause {
#sum
}
)
};
let impl_fn_product = {
let where_clause = if is_generic {
quote!(where #type_path: core::ops::Mul<Output=#type_path>)
} else {
quote!()
};
let mut tuple_indexes = tuple_indexes.to_vec();
let mut struct_fields = fields.to_vec();
let last_tuple_field = tuple_indexes.pop();
let last_struct_field = struct_fields.pop();
let product = if is_tuple {
quote!(#(self.#tuple_indexes *)* self.#last_tuple_field)
} else {
quote!(#(self.#struct_fields *)* self.#last_struct_field)
};
quote!(
#[inline]
pub fn product(self) -> #type_path #where_clause {
#product
}
)
};
let impl_fn_dot = {
let where_clause = if is_generic {
quote!(
where #type_path: core::ops::Add<Output=#type_path>
+ core::ops::Mul<Output=#type_path>
)
} else {
quote!()
};
quote!(
#[inline]
pub fn dot(self, other: Self) -> #type_path #where_clause {
(self * other).sum()
}
)
};
let impl_fn_length = if fields_count <= 4 {
let fn_length_squared = {
let where_clause = if is_generic {
quote!(
where #type_path: core::marker::Copy
+ core::ops::Add<Output=#type_path>
+ core::ops::Mul<Output=#type_path>
)
} else {
quote!()
};
quote!(
#[inline]
pub fn length_squared(self) -> #type_path #where_clause {
self.dot(self)
}
)
};
let fn_length = if is_generic || is_float {
let where_clause = if is_generic {
quote!(
where #type_path: core::marker::Copy
+ num_traits::real::Real
+ core::ops::Add<Output=#type_path>
+ core::ops::Mul<Output=#type_path>
)
} else {
quote!()
};
quote!(
#[inline]
pub fn length(self) -> #type_path #where_clause {
self.dot(self).sqrt()
}
)
} else {
quote!()
};
let fn_length_recip = if is_generic || is_float {
let where_clause_length_recip = if is_generic {
quote!(
where #type_path: core::marker::Copy
+ num_traits::One
+ num_traits::real::Real
+ core::ops::Add<Output=#type_path>
+ core::ops::Mul<Output=#type_path>
)
} else {
quote!()
};
let one = if is_float {
quote!(1.)
} else {
quote!(#type_path::one())
};
quote!(
#[inline]
pub fn length_recip(self) -> #type_path #where_clause_length_recip {
#one / self.length()
}
)
} else {
quote!()
};
quote!(
#fn_length_squared
#fn_length
#fn_length_recip
)
} else {
quote!()
};
let impl_fn_distance = if fields_count <= 4 && (is_float || is_generic) {
let fn_distance_squared = {
let where_clause = if is_generic {
quote!(
where #type_path: core::marker::Copy
+ num_traits::real::Real
+ core::ops::Add<Output=#type_path>
+ core::ops::Sub<Output=#type_path>
+ core::ops::Mul<Output=#type_path>
)
} else {
quote!()
};
quote!(
#[inline]
pub fn distance_squared(self, other: Self) -> #type_path #where_clause {
(self - other).length_squared()
}
)
};
let fn_distance = {
let where_clause = if is_generic {
quote!(
where #type_path: core::marker::Copy
+ num_traits::real::Real
+ core::ops::Add<Output=#type_path>
+ core::ops::Sub<Output=#type_path>
+ core::ops::Mul<Output=#type_path>
)
} else {
quote!()
};
quote!(
#[inline]
pub fn distance(self, other: Self) -> #type_path #where_clause {
(self - other).length()
}
)
};
quote!(
#fn_distance_squared
#fn_distance
)
} else {
quote!()
};
let impl_fn_min_max = {
let fn_min = {
let where_clause = if is_generic {
quote!(
where #type_path: core::marker::Copy
+ core::cmp::PartialOrd
)
} else {
quote!()
};
let inner_new = if is_tuple {
quote!(
#(if self.#tuple_indexes > other.#tuple_indexes {
other.#tuple_indexes
} else {
self.#tuple_indexes
}),*
)
} else {
quote!(
#(if self.#fields > other.#fields {
other.#fields
} else {
self.#fields
}),*
)
};
quote!(
#[inline]
pub fn min(self, other: Self) -> Self #where_clause {
Self::new(#inner_new)
}
)
};
let fn_max = {
let where_clause = if is_generic {
quote!(
where #type_path: core::marker::Copy
+ core::cmp::PartialOrd
)
} else {
quote!()
};
let inner_new = if is_tuple {
quote!(
#(if self.#tuple_indexes < other.#tuple_indexes {
other.#tuple_indexes
} else {
self.#tuple_indexes
}),*
)
} else {
quote!(
#(if self.#fields < other.#fields {
other.#fields
} else {
self.#fields
}),*
)
};
quote!(
#[inline]
pub fn max(self, other: Self) -> Self #where_clause {
Self::new(#inner_new)
}
)
};
let fn_min_elem = {
let where_clause = if is_generic {
quote!(
where #type_path: core::marker::Copy
+ core::cmp::PartialOrd
)
} else {
quote!()
};
quote!(
#[inline]
pub fn min_elem(self) -> #type_path #where_clause {
let mut iter = self.iter();
let mut min = iter.next().cloned().unwrap();
for &x in iter {
if x < min {
min = x;
}
}
min
}
)
};
let fn_max_elem = {
let where_clause = if is_generic {
quote!(
where #type_path: core::marker::Copy
+ core::cmp::PartialOrd
)
} else {
quote!()
};
quote!(
#[inline]
pub fn max_elem(self) -> #type_path #where_clause {
let mut iter = self.iter();
let mut max = iter.next().cloned().unwrap();
for &x in iter {
if x > max {
max = x;
}
}
max
}
)
};
quote!(
#fn_min
#fn_max
#fn_min_elem
#fn_max_elem
)
};
let impl_fn_map = {
let inner_new = if is_tuple {
quote!(#(f(self.#tuple_indexes)),*)
} else {
quote!(#(f(self.#fields)),*)
};
if is_generic {
let predicates = where_clause.as_ref().map(|w| w.predicates.clone());
let m_bounds = match &predicates {
Some(punct) => match punct.first() {
Some(syn::WherePredicate::Type(ty)) => {
let bounds = ty.bounds.clone();
quote!(M: #bounds, )
}
Some(_) => unreachable!(),
None => quote!(),
},
_ => quote!(),
};
quote!(
#[inline]
pub fn map<M, F>(self, mut f: F) -> #ident<M>
where #m_bounds F: core::ops::FnMut(#type_path) -> M {
#ident::new(#inner_new)
}
)
} else {
quote!()
}
};
let impl_fn_apply = {
let where_clause = if is_generic {
quote!(
where #type_path: core::marker::Copy
, F: core::ops::FnMut(#type_path) -> #type_path
)
} else {
quote!(
where F: core::ops::FnMut(#type_path) -> #type_path
)
};
let body = if is_tuple {
quote!(#(self.#tuple_indexes = f(self.#tuple_indexes);)*)
} else {
quote!(#(self.#fields = f(self.#fields);)*)
};
quote!(
#[inline]
pub fn apply<F>(&mut self, mut f: F) #where_clause {
#body
}
)
};
let impl_fn_abs = if is_generic || is_signed_primitive {
let where_clause = if is_generic {
quote!(where #type_path: num_traits::sign::Signed)
} else {
quote!()
};
let inner_new = if is_tuple {
quote!(#(self.#tuple_indexes.abs()),*)
} else {
quote!(#(self.#fields.abs()),*)
};
quote!(
#[inline]
pub fn abs(self) -> Self #where_clause {
Self::new(#inner_new)
}
)
} else {
quote!()
};
let impl_fn_normalize = if fields_count <= 4 && (is_generic || is_float) {
let where_clause = if is_generic {
quote!(
where #type_path: core::ops::Add<Output=#type_path>
+ num_traits::real::Real + num_traits::identities::One
)
} else {
quote!()
};
let inv = if is_generic {
quote!(let inv = #type_path::one() / self.length();)
} else {
quote!(let inv = 1.0 / self.length();)
};
quote!(
#[inline]
pub fn normalize(self) -> Self #where_clause {
#inv
self * inv
}
)
} else {
quote!()
};
let impl_fn_clamp = {
let where_clause = if is_generic {
quote!(
where #type_path: core::cmp::PartialOrd
)
} else {
quote!()
};
let inner_new = if is_tuple {
quote!(#(_clamp(self.#tuple_indexes, min.#tuple_indexes, max.#tuple_indexes)),*)
} else {
quote!(#(_clamp(self.#fields, min.#fields, max.#fields)),*)
};
quote!(
pub fn clamp(self, min: Self, max: Self) -> Self #where_clause {
fn _clamp<T>(x: T, min: T, max: T) -> T where T: core::cmp::PartialOrd {
assert!(min <= max);
let mut x = x;
if x < min {
x = min;
}
if x > max {
x = max;
}
x
}
Self::new(#inner_new)
}
)
};
let impl_fn_cross = if fields_count == 3 {
let where_clause = if is_generic {
quote!(where #type_path: Copy + core::ops::Mul<Output=T> + core::ops::Sub<Output=T>)
} else {
quote!()
};
quote!(
#[inline]
pub fn cross(self, other: Self) -> Self #where_clause {
let ((x, y, z), (u, v, w)) = (self.into(), other.into());
(y * w - z * v, z * u - x * w, x * v - y * u).into()
}
)
} else {
quote!()
};
let impl_fn_is_nan = if is_generic || is_float {
let where_clause = if is_generic {
quote!(where #type_path: num_traits::float::Float)
} else {
quote!()
};
quote!(
#[inline]
pub fn is_nan(self) -> bool #where_clause {
self.iter().any(|&x| x.is_nan())
}
)
} else {
quote!()
};
quote!(
impl #generics #ident #generics #where_clause {
#impl_consts
#impl_fn_new
#impl_fn_splat
#impl_fn_unit_field
#impl_fn_as_ptr
#impl_fn_as_slice
#impl_fn_sum
#impl_fn_product
#impl_fn_dot
#impl_fn_length
#impl_fn_distance
#impl_fn_min_max
#impl_fn_map
#impl_fn_apply
#impl_fn_abs
#impl_fn_normalize
#impl_fn_clamp
#impl_fn_cross
#impl_fn_is_nan
}
)
};
let expanded = quote! {
#item_repr_c
#impl_as_ref
#impl_de_ref
#impl_borrow
#impl_from_inner
#impl_from_tuple_array
#impl_into_tuple_array
#impl_unop
#impl_binop
#impl_binop_inner
#impl_binop_commutative
#impl_fn_shared
};
Ok(expanded.into())
}
#[derive(Debug)]
struct ParseResult {
ident: Ident,
generics: Option<Generics>,
where_clause: Option<WhereClause>,
generic_ident: Option<Ident>,
type_path: Option<Path>,
fields: Vec<Ident>,
fields_count: usize,
tuple_indexes: Vec<Index>,
compile_error: Option<TokenStream>,
is_tuple: bool,
is_generic: bool,
is_primitive: bool,
is_signed_primitive: bool,
is_float: bool,
}
impl ParseResult {
fn dummy() -> Self {
ParseResult {
ident: Ident::new("dummy", Span::call_site()),
generics: None,
where_clause: None,
generic_ident: None,
type_path: None,
fields: Vec::new(),
fields_count: 0,
tuple_indexes: Vec::new(),
compile_error: None,
is_tuple: false,
is_generic: false,
is_primitive: false,
is_signed_primitive: false,
is_float: false,
}
}
}
fn parse_struct(item: &ItemStruct) -> ParseResult {
let mut ret = ParseResult::dummy();
ret.ident = item.ident.clone();
ret.generics = Some(item.generics.clone());
let mut where_clause = item.generics.where_clause.clone();
if let Some(v) = where_clause.as_mut() {
if !v.predicates.is_empty() && !v.predicates.trailing_punct() {
(*v).predicates.push_punct(Default::default());
}
}
ret.where_clause = where_clause;
macro_rules! err_if_none {
($span:expr, $fmt:literal $(,)? $($arg:expr),*) => {{
if ret.compile_error.is_none() {
ret.compile_error = Some(err!($span, $fmt $(, $arg),*));
return ret;
}
}}
}
match &item.generics.params.first() {
Some(generic) => {
use syn::GenericParam::*;
match generic {
Type(ty) => {
ret.is_generic = true;
ret.generic_ident = Some(ty.ident.clone());
}
Lifetime(lifetime) => {
err_if_none!(
lifetime.span(),
"expected generic type, but found generic lifetime"
);
}
Const(cons) => {
err_if_none!(
cons.span(),
"expected generic type, but found generic const"
);
}
}
}
None => (),
}
match &item.fields {
Fields::Unit => {
err_if_none!(item.span(), "unit struct cannot be made into a vector");
}
Fields::Named(fields) => {
if fields.named.is_empty() {
err_if_none!(
fields.span(),
"expected at least one field, but found nothing"
);
} else {
let iter = fields.named.iter();
let mut prev = fields.named.first().unwrap();
for x in iter {
match &x.ty {
Type::Path(type_path) => {
if !x.ty.eq(&prev.ty) {
err_if_none!(
fields.span(),
"all fields of this struct should be of the same type"
);
} else {
ret.fields.push(x.ident.as_ref().unwrap().clone());
ret.fields_count += 1;
ret.type_path = Some(type_path.path.clone());
}
}
_ => {
if ret.is_generic {
err_if_none!(
fields.span(),
"expected at most one generic type, but found others",
);
} else {
err_if_none!(
fields.span(),
"expected owned primitive type, but found others"
);
}
}
}
prev = x;
}
}
}
Fields::Unnamed(fields) => {
ret.is_tuple = true;
if fields.unnamed.is_empty() {
err_if_none!(
fields.span(),
"expected at least one field, but found nothing"
);
} else {
let iter = fields.unnamed.iter();
let mut prev = fields.unnamed.first().unwrap();
for x in iter {
match &x.ty {
Type::Path(type_path) => {
if !x.ty.eq(&prev.ty) {
err_if_none!(
fields.span(),
"all fields of this struct should be of the same type"
);
} else {
ret.tuple_indexes.push(Index::from(ret.fields_count));
ret.fields.push(format_ident!("a{}", ret.fields_count));
ret.fields_count += 1;
ret.type_path = Some(type_path.path.clone());
}
}
_ => {
if ret.is_generic {
err_if_none!(
fields.span(),
"expected at most one generic type, but found others",
);
} else {
err_if_none!(
fields.span(),
"expected owned primitive type, but found others"
);
}
}
}
prev = x;
}
}
}
}
if ret.fields_count == 1 {
err_if_none!(
item.fields.span(),
"create a vector with only one field is not allowed"
)
}
if !ret.is_generic {
let checked = check_primitive(&ret.type_path);
ret.is_primitive = checked.0;
ret.is_signed_primitive = checked.1;
ret.is_float = checked.2;
if !ret.is_primitive {
err_if_none!(
item.fields.span(),
"expected numeric primitive type, but found others"
);
}
}
ret
}