strict_num_extended_macros/
lib.rs

1//! # Proc Macro Implementation
2//!
3//! Provides complete procedural macro code generation for strict-num-extended
4#![cfg_attr(docsrs, feature(doc_cfg))]
5
6use proc_macro::TokenStream;
7use quote::quote;
8use syn::parse_macro_input;
9
10mod arithmetic;
11mod comparison;
12mod config;
13mod constants;
14mod conversion;
15mod doc_generator;
16mod finite_float;
17mod finite_float_trait;
18mod float_conversion;
19mod fromstr_impl;
20mod generator;
21mod option_arithmetic;
22mod result_arithmetic;
23mod type_aliases;
24mod unary_ops;
25
26use arithmetic::{generate_arithmetic_impls, generate_neg_impls};
27use comparison::{generate_comparison_traits, generate_concrete_comparison_traits};
28use config::TypeConfig;
29use constants::generate_constants;
30use conversion::generate_conversion_traits;
31use finite_float::{
32    generate_concrete_impls, generate_concrete_serde_impls, generate_concrete_structs,
33};
34use finite_float_trait::{generate_finite_float_impls, generate_finite_float_trait};
35use float_conversion::{
36    generate_as_f32_primitive_methods, generate_as_f32_type_methods,
37    generate_as_f64_primitive_methods, generate_as_f64_type_methods,
38    generate_try_into_f32_type_methods,
39};
40use fromstr_impl::{
41    generate_fromstr_traits, generate_parse_error_from_impls, generate_parse_error_type,
42};
43use option_arithmetic::generate_option_arithmetic_impls;
44use result_arithmetic::generate_result_arithmetic_impls;
45use type_aliases::generate_type_aliases;
46use unary_ops::{
47    generate_abs_impls, generate_cos_impls, generate_signum_impls, generate_sin_impls,
48    generate_tan_impls,
49};
50
51/// Generates common definitions (constants)
52fn generate_common_definitions() -> proc_macro2::TokenStream {
53    quote! {
54        use core::marker::PhantomData;
55        use core::ops::{Add, Sub, Mul, Div, Neg};
56
57        // ========== f64 boundary bit representation constants ==========
58        const F64_MIN_BITS: i64 = f64::MIN.to_bits() as i64;
59        const F64_MAX_BITS: i64 = f64::MAX.to_bits() as i64;
60        const ZERO_BITS: i64 = 0.0f64.to_bits() as i64;
61        // Use minimum positive normal number instead of EPSILON (to avoid excluding very small positive numbers)
62        const F64_MIN_POSITIVE_BITS: i64 = f64::MIN_POSITIVE.to_bits() as i64;
63        const F64_NEG_MIN_POSITIVE_BITS: i64 = (-f64::MIN_POSITIVE).to_bits() as i64;
64        const ONE_BITS: i64 = 1.0f64.to_bits() as i64;
65        const NEG_ONE_BITS: i64 = (-1.0f64).to_bits() as i64;
66
67        // ========== f32 boundary bit representation constants (stored as f64) ==========
68        const F32_MIN_BITS: i64 = (f32::MIN as f64).to_bits() as i64;
69        const F32_MAX_BITS: i64 = (f32::MAX as f64).to_bits() as i64;
70        // Use minimum positive normal number instead of EPSILON
71        const F32_MIN_POSITIVE_BITS: i64 = (f32::MIN_POSITIVE as f64).to_bits() as i64;
72        const F32_NEG_MIN_POSITIVE_BITS: i64 = ((-f32::MIN_POSITIVE) as f64).to_bits() as i64;
73    }
74}
75
76/// Generates the `FloatError` type and its trait implementations
77fn generate_error_type() -> proc_macro2::TokenStream {
78    quote! {
79        /// Errors that can occur when creating or operating on finite floats
80        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
81        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
82        pub enum FloatError {
83            /// Value is NaN (Not a Number)
84            NaN,
85            /// Value is positive infinity
86            PosInf,
87            /// Value is negative infinity
88            NegInf,
89            /// Value is outside the valid range for this type
90            OutOfRange,
91            /// Right-hand side operand is None in Option arithmetic
92            NoneOperand,
93        }
94
95        impl core::fmt::Display for FloatError {
96            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
97                match self {
98                    FloatError::NaN => write!(f, "value is NaN (Not a Number)"),
99                    FloatError::PosInf => write!(f, "value is positive infinity"),
100                    FloatError::NegInf => write!(f, "value is negative infinity"),
101                    FloatError::OutOfRange => write!(f, "value is outside the valid range for this type"),
102                    FloatError::NoneOperand => write!(f, "right-hand side operand is None in Option arithmetic"),
103                }
104            }
105        }
106
107        #[cfg(feature = "std")]
108        impl std::error::Error for FloatError {}
109    }
110}
111
112/// Generates zero-sized constraint marker types dynamically from config
113fn generate_constraint_markers(config: &TypeConfig) -> proc_macro2::TokenStream {
114    let markers = config.constraints.iter().map(|constraint| {
115        let name = &constraint.name;
116        quote! {
117            #[doc(hidden)]
118            #[derive(Debug, Clone, Copy)]
119            pub(crate) struct #name;
120        }
121    });
122
123    quote! {
124        #(#markers)*
125    }
126}
127
128/// Main macro: generates finite floating-point types with automatic `is_finite()` checking.
129#[proc_macro]
130pub fn generate_finite_float_types(input: TokenStream) -> TokenStream {
131    let config = parse_macro_input!(input as TypeConfig);
132
133    // Collect all code to be generated
134    let mut all_code = vec![
135        generate_common_definitions(),
136        generate_error_type(),
137        generate_parse_error_type(),
138        generate_parse_error_from_impls(),
139        generate_constraint_markers(&config),
140        generate_concrete_structs(&config),
141        generate_comparison_traits(),
142    ];
143
144    // Generate concrete struct implementations (includes new, get, new_unchecked, new_const)
145    all_code.push(generate_concrete_impls(&config));
146    all_code.push(generate_concrete_serde_impls(&config));
147    all_code.push(generate_concrete_comparison_traits(&config));
148
149    // Generate type-safe arithmetic operations
150    all_code.push(generate_arithmetic_impls(&config));
151
152    // Generate arithmetic operations for Option types
153    all_code.push(generate_option_arithmetic_impls(&config));
154
155    // Generate arithmetic operations for Result types
156    all_code.push(generate_result_arithmetic_impls(&config));
157
158    // Generate negation operations
159    all_code.push(generate_neg_impls(&config));
160
161    // Generate unary operations (abs, signum)
162    all_code.push(generate_abs_impls(&config));
163    all_code.push(generate_signum_impls(&config));
164
165    // Generate trigonometric operations (sin, cos, tan)
166    all_code.push(generate_sin_impls(&config));
167    all_code.push(generate_cos_impls(&config));
168    all_code.push(generate_tan_impls(&config));
169
170    // Generate negation operations for Result types
171    // Note: Cannot implement Neg for Result<T, E> due to orphan rules
172    // Users should use .map() instead: result.map(|x| -x)
173    // all_code.push(generate_result_neg_impls(&config));
174
175    // Generate F32/F64 conversion methods
176    all_code.push(generate_as_f32_primitive_methods(&config));
177    all_code.push(generate_as_f64_primitive_methods(&config));
178    all_code.push(generate_as_f32_type_methods(&config));
179    all_code.push(generate_as_f64_type_methods(&config));
180    all_code.push(generate_try_into_f32_type_methods(&config));
181
182    // Generate From/TryFrom traits
183    all_code.push(generate_conversion_traits(&config));
184
185    // Generate FromStr trait implementations
186    all_code.push(generate_fromstr_traits(&config));
187
188    // Generate FiniteFloat trait and implementations
189    all_code.push(generate_finite_float_trait(&config));
190    all_code.push(generate_finite_float_impls(&config));
191
192    // Generate constants
193    all_code.push(generate_constants(&config));
194
195    // Generate type aliases
196    all_code.push(generate_type_aliases(&config));
197
198    // Combine all code
199    let expanded = quote! {
200        #(#all_code)*
201    };
202
203    TokenStream::from(expanded)
204}