use crate::config::TypeConfig;
use crate::generator::{
filter_constraint_types_by_float, find_constraint_def, for_all_constraint_float_types,
};
use proc_macro2::Ident;
use quote::{format_ident, quote};
pub fn generate_conversion_traits(config: &TypeConfig) -> proc_macro2::TokenStream {
let mut all_code = vec![];
all_code.push(generate_constraint_to_primitive_from(config));
all_code.push(generate_primitive_to_constraint_tryfrom(config));
all_code.push(generate_constraint_to_constraint_traits(config));
all_code.push(generate_f32_to_f64_from(config));
all_code.push(generate_f64_to_f32_tryfrom(config));
all_code.push(generate_f32_to_f64_constraint_tryfrom(config));
all_code.push(generate_f64_to_f32_constraint_tryfrom(config));
quote! { #(#all_code)* }
}
fn is_subset_constraint(
src_lower: Option<f64>,
src_upper: Option<f64>,
src_excludes_zero: bool,
dst_lower: Option<f64>,
dst_upper: Option<f64>,
dst_excludes_zero: bool,
) -> bool {
let lower_contains = match (src_lower, dst_lower) {
(Some(src), Some(dst)) => src >= dst,
(None, Some(_)) => false,
_ => true,
};
let upper_contains = match (src_upper, dst_upper) {
(Some(src), Some(dst)) => src <= dst,
(None, Some(_)) => false,
_ => true,
};
let zero_compatible = !dst_excludes_zero || src_excludes_zero;
lower_contains && upper_contains && zero_compatible
}
fn generate_constraint_to_primitive_from(config: &TypeConfig) -> proc_macro2::TokenStream {
let impls = for_all_constraint_float_types(config, |type_name, float_type, _| {
let alias = format_ident!("{}{}", type_name, float_type.to_string().to_uppercase());
quote! {
impl From<#alias> for #float_type {
#[inline]
fn from(value: #alias) -> Self {
value.get()
}
}
}
});
quote! { #(#impls)* }
}
fn generate_primitive_to_constraint_tryfrom(config: &TypeConfig) -> proc_macro2::TokenStream {
let impls = for_all_constraint_float_types(config, |type_name, float_type, _| {
let alias = format_ident!("{}{}", type_name, float_type.to_string().to_uppercase());
quote! {
impl TryFrom<#float_type> for #alias {
type Error = FloatError;
#[inline]
fn try_from(value: #float_type) -> Result<Self, Self::Error> {
Self::new(value)
}
}
}
});
quote! { #(#impls)* }
}
fn generate_constraint_to_constraint_traits(config: &TypeConfig) -> proc_macro2::TokenStream {
let mut all_impls = vec![];
for float_type in &["f32", "f64"] {
let float_ident = Ident::new(float_type, proc_macro2::Span::call_site());
let types = filter_constraint_types_by_float(config, &float_ident);
for src_type in &types {
for dst_type in &types {
if src_type.type_name.eq(&dst_type.type_name) {
continue; }
let src_alias = format_ident!(
"{}{}",
src_type.type_name,
float_type.to_string().to_uppercase()
);
let dst_alias = format_ident!(
"{}{}",
dst_type.type_name,
float_type.to_string().to_uppercase()
);
let src_constraint = find_constraint_def(config, &src_type.constraint_name);
let dst_constraint = find_constraint_def(config, &dst_type.constraint_name);
let is_safe = is_subset_constraint(
src_constraint.bounds.lower,
src_constraint.bounds.upper,
src_constraint.excludes_zero,
dst_constraint.bounds.lower,
dst_constraint.bounds.upper,
dst_constraint.excludes_zero,
);
if is_safe {
all_impls.push(quote! {
impl From<#src_alias> for #dst_alias {
#[inline]
fn from(value: #src_alias) -> Self {
unsafe { Self::new_unchecked(value.get()) }
}
}
});
} else {
all_impls.push(quote! {
impl TryFrom<#src_alias> for #dst_alias {
type Error = FloatError;
#[inline]
fn try_from(value: #src_alias) -> Result<Self, Self::Error> {
Self::new(value.get())
}
}
});
}
}
}
}
quote! { #(#all_impls)* }
}
fn generate_f32_to_f64_from(config: &TypeConfig) -> proc_macro2::TokenStream {
let impls = for_all_constraint_float_types(config, |type_name, float_type, _| {
if *float_type != "f32" {
return quote! {};
}
let f32_alias = format_ident!("{}F32", type_name);
let f64_alias = format_ident!("{}F64", type_name);
quote! {
impl From<#f32_alias> for #f64_alias {
#[inline]
fn from(value: #f32_alias) -> Self {
value.as_f64_type()
}
}
}
});
quote! { #(#impls)* }
}
fn generate_f64_to_f32_tryfrom(config: &TypeConfig) -> proc_macro2::TokenStream {
let impls = for_all_constraint_float_types(config, |type_name, float_type, _| {
if *float_type != "f64" {
return quote! {};
}
let f32_alias = format_ident!("{}F32", type_name);
let f64_alias = format_ident!("{}F64", type_name);
quote! {
impl TryFrom<#f64_alias> for #f32_alias {
type Error = FloatError;
#[inline]
fn try_from(value: #f64_alias) -> Result<Self, Self::Error> {
value.try_into_f32_type()
}
}
}
});
quote! { #(#impls)* }
}
fn generate_f32_to_f64_constraint_tryfrom(config: &TypeConfig) -> proc_macro2::TokenStream {
let impls = for_all_constraint_float_types(config, |type_name, float_type, _| {
if *float_type != "f64" {
return quote! {};
}
let f64_alias = format_ident!("{}F64", type_name);
quote! {
impl TryFrom<f32> for #f64_alias {
type Error = FloatError;
#[inline]
fn try_from(value: f32) -> Result<Self, Self::Error> {
Self::new(value as f64)
}
}
}
});
quote! { #(#impls)* }
}
fn generate_f64_to_f32_constraint_tryfrom(config: &TypeConfig) -> proc_macro2::TokenStream {
let impls = for_all_constraint_float_types(config, |type_name, float_type, _| {
if *float_type != "f32" {
return quote! {};
}
let f32_alias = format_ident!("{}F32", type_name);
quote! {
impl TryFrom<f64> for #f32_alias {
type Error = FloatError;
#[inline]
fn try_from(value: f64) -> Result<Self, Self::Error> {
Self::new(value as f32)
}
}
}
});
quote! { #(#impls)* }
}