checked_rs_macro_impl/
params.rs

1use convert_case::{Case, Casing};
2use proc_macro2::TokenStream;
3use quote::{format_ident, ToTokens};
4use syn::spanned::Spanned;
5
6pub mod as_soft_or_hard;
7pub mod behavior_arg;
8pub mod derived_traits;
9pub mod lower_or_min;
10pub mod min_or_max;
11pub mod number_arg;
12pub mod number_arg_range;
13pub mod number_kind;
14pub mod number_value;
15pub mod number_value_range;
16pub mod panic_or_panicking;
17pub mod saturate_or_saturating;
18pub mod semi_or_colon;
19pub mod upper_or_max;
20
21pub use as_soft_or_hard::*;
22pub use behavior_arg::*;
23pub use derived_traits::*;
24pub use lower_or_min::*;
25pub use min_or_max::*;
26pub use number_arg::*;
27pub use number_arg_range::*;
28pub use number_kind::*;
29pub use number_value::*;
30pub use number_value_range::*;
31pub use panic_or_panicking::*;
32pub use saturate_or_saturating::*;
33pub use semi_or_colon::*;
34pub use upper_or_max::*;
35
36/// Custom keywords used when parsing the `clamped` attribute.
37pub mod kw {
38    syn::custom_keyword!(derive);
39    syn::custom_keyword!(default);
40    syn::custom_keyword!(behavior);
41    syn::custom_keyword!(lower);
42    syn::custom_keyword!(upper);
43    syn::custom_keyword!(min);
44    syn::custom_keyword!(max);
45    syn::custom_keyword!(Soft);
46    syn::custom_keyword!(Hard);
47    syn::custom_keyword!(Saturate);
48    syn::custom_keyword!(Saturating);
49    syn::custom_keyword!(Panic);
50    syn::custom_keyword!(Panicking);
51    syn::custom_keyword!(MIN);
52    syn::custom_keyword!(MAX);
53}
54
55#[derive(Clone)]
56pub struct Params {
57    pub integer: NumberKind,
58    pub derived_traits: Option<DerivedTraits>,
59    pub vis: syn::Visibility,
60    pub ident: syn::Ident,
61    pub as_soft_or_hard: Option<AsSoftOrHard>,
62    pub behavior: BehaviorArg,
63    pub default_val: Option<NumberValue>,
64    pub lower_limit_val: NumberValue,
65    pub upper_limit_val: NumberValue,
66    pub full_coverage: bool,
67    pub exact_values: Option<Vec<NumberValue>>,
68    pub valid_ranges: Option<Vec<NumberValueRange>>,
69}
70
71impl std::fmt::Debug for Params {
72    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        f.debug_struct("Params")
74            .field("integer", &self.integer)
75            .field("derived_traits", &self.derived_traits)
76            .field("vis", &self.vis.to_token_stream().to_string())
77            .field("ident", &self.ident)
78            .field("as_soft_or_hard", &self.as_soft_or_hard)
79            .field("behavior", &self.behavior)
80            .field("default_val", &self.default_val)
81            .field("lower_limit", &self.lower_limit_val)
82            .field("upper_limit", &self.upper_limit_val)
83            .field("full_coverage", &self.full_coverage)
84            .field("exact_values", &self.exact_values)
85            .field("valid_ranges", &self.valid_ranges)
86            .finish()
87    }
88}
89
90impl Params {
91    pub fn mod_ident(&self) -> syn::Ident {
92        format_ident!("clamped_{}", self.ident.to_string().to_case(Case::Snake))
93    }
94
95    pub fn guard_ident(&self) -> syn::Ident {
96        format_ident!("{}Guard", &self.ident)
97    }
98
99    pub fn value_ident(&self) -> syn::Ident {
100        format_ident!("{}Value", &self.ident)
101    }
102
103    pub fn other_ident(&self, other_name: &syn::Ident) -> syn::Ident {
104        format_ident!("{}{}", other_name, self.value_ident())
105    }
106
107    pub fn default_val_token(&self) -> TokenStream {
108        let kind = self.integer;
109
110        // if the `default_val` was provided, use it
111        if let Some(val) = self.default_val {
112            return val.to_token_stream();
113        }
114
115        let zero = NumberValue::new(kind, 0);
116
117        // if zero is in the exact value list or allowed by the ranges, use zero
118        if let Some(exacts) = &self.exact_values {
119            if exacts.contains(&zero) {
120                return zero.to_token_stream();
121            }
122        }
123
124        if let Some(ranges) = &self.valid_ranges {
125            for range in ranges {
126                if range.contains(&zero) {
127                    return zero.to_token_stream();
128                }
129            }
130        }
131
132        // otherwise use the `lower_limit`
133        self.lower_limit_token()
134    }
135
136    /// Output the lower limit value as a bare literal in a token stream.
137    pub fn lower_limit_token(&self) -> TokenStream {
138        syn::parse_str(&self.lower_limit_val.to_string()).unwrap()
139    }
140
141    /// Output the upper limit value as a bare literal in a token stream.
142    pub fn upper_limit_token(&self) -> TokenStream {
143        syn::parse_str(&self.upper_limit_val.to_string()).unwrap()
144    }
145
146    pub fn first_uniq_val(&self) -> NumberValue {
147        let exact_min = if let Some(exacts) = &self.exact_values {
148            exacts.first().copied()
149        } else {
150            None
151        };
152
153        let range_min = if let Some(ranges) = &self.valid_ranges {
154            ranges.first().map(|range| range.first_val())
155        } else {
156            None
157        };
158
159        match (exact_min, range_min) {
160            (Some(exact), Some(range)) => {
161                if exact < range {
162                    exact
163                } else {
164                    range
165                }
166            }
167            (Some(exact), None) => exact,
168            (None, Some(range)) => range,
169            (None, None) => self.lower_limit_val,
170        }
171    }
172
173    pub fn last_uniq_val(&self) -> NumberValue {
174        let exact_max = if let Some(exacts) = &self.exact_values {
175            exacts.last().copied()
176        } else {
177            None
178        };
179
180        let range_max = if let Some(ranges) = &self.valid_ranges {
181            ranges.last().map(|range| range.last_val())
182        } else {
183            None
184        };
185
186        match (exact_max, range_max) {
187            (Some(exact), Some(range)) => {
188                if exact > range {
189                    exact
190                } else {
191                    range
192                }
193            }
194            (Some(exact), None) => exact,
195            (None, Some(range)) => range,
196            (None, None) => self.upper_limit_val,
197        }
198    }
199
200    /// Validate that an arbitrary value is within the lower and upper limit.
201    pub fn check_if_out_of_bounds<T: Spanned + ToTokens>(
202        &self,
203        ast: &T,
204        value: NumberValue,
205    ) -> syn::Result<()> {
206        let lower = self.lower_limit_val;
207        let upper = self.upper_limit_val;
208
209        if value < lower {
210            return Err(syn::Error::new(
211                ast.span(),
212                format!(
213                    "{:?} value: {} is less than lower limit: {}",
214                    self.integer, value, lower
215                ),
216            ));
217        }
218
219        if value > upper {
220            return Err(syn::Error::new(
221                ast.span(),
222                format!(
223                    "{:?} value: {} is greater than upper limit: {}",
224                    self.integer, value, upper
225                ),
226            ));
227        }
228
229        Ok(())
230    }
231
232    pub fn is_signed(&self) -> bool {
233        matches!(
234            self.integer,
235            NumberKind::I8
236                | NumberKind::I16
237                | NumberKind::I32
238                | NumberKind::I64
239                | NumberKind::I128
240                | NumberKind::ISize
241        )
242    }
243
244    /// Check if the number kind is `u16` or smaller.
245    pub fn is_u16_or_smaller(&self) -> bool {
246        matches!(self.integer, NumberKind::U8 | NumberKind::U16)
247    }
248
249    /// Check if the number kind is `u16` or larger.
250    pub fn is_u16_or_larger(&self) -> bool {
251        matches!(
252            self.integer,
253            NumberKind::U16 | NumberKind::U32 | NumberKind::U64 | NumberKind::U128
254        )
255    }
256
257    /// Check if the number kind is `u32` or smaller.
258    pub fn is_u32_or_smaller(&self) -> bool {
259        matches!(
260            self.integer,
261            NumberKind::U8 | NumberKind::U16 | NumberKind::U32 | NumberKind::USize
262        )
263    }
264
265    /// Check if the number kind is `u32` or larger.
266    pub fn is_u32_or_larger(&self) -> bool {
267        matches!(
268            self.integer,
269            NumberKind::U32 | NumberKind::USize | NumberKind::U64 | NumberKind::U128
270        )
271    }
272
273    /// Check if the number kind is `u64` or smaller.
274    pub fn is_u64_or_smaller(&self) -> bool {
275        matches!(
276            self.integer,
277            NumberKind::U8
278                | NumberKind::U16
279                | NumberKind::U32
280                | NumberKind::USize
281                | NumberKind::U64
282        )
283    }
284
285    /// Check if the number kind is `u64` or larger.
286    pub fn is_u64_or_larger(&self) -> bool {
287        matches!(self.integer, NumberKind::U64 | NumberKind::U128)
288    }
289
290    pub fn is_usize_or_smaller(&self) -> bool {
291        matches!(
292            self.integer,
293            NumberKind::U8 | NumberKind::U16 | NumberKind::U32 | NumberKind::USize
294        )
295    }
296
297    pub fn is_usize_or_larger(&self) -> bool {
298        matches!(
299            self.integer,
300            NumberKind::USize | NumberKind::U64 | NumberKind::U128
301        )
302    }
303
304    /// Check if the number kind is `u128` or smaller.
305    pub fn is_u128_or_smaller(&self) -> bool {
306        matches!(
307            self.integer,
308            NumberKind::U8
309                | NumberKind::U16
310                | NumberKind::U32
311                | NumberKind::U64
312                | NumberKind::USize
313                | NumberKind::U128
314        )
315    }
316
317    pub fn is_i16_or_smaller(&self) -> bool {
318        matches!(
319            self.integer,
320            NumberKind::I8 | NumberKind::I16 | NumberKind::U8
321        )
322    }
323
324    pub fn is_i16_or_larger(&self) -> bool {
325        matches!(
326            self.integer,
327            NumberKind::I16 | NumberKind::I32 | NumberKind::I64 | NumberKind::I128
328        )
329    }
330
331    pub fn is_i32_or_smaller(&self) -> bool {
332        matches!(
333            self.integer,
334            NumberKind::I8
335                | NumberKind::I16
336                | NumberKind::I32
337                | NumberKind::ISize
338                | NumberKind::U8
339                | NumberKind::U16
340        )
341    }
342
343    pub fn is_i32_or_larger(&self) -> bool {
344        matches!(
345            self.integer,
346            NumberKind::I32 | NumberKind::I64 | NumberKind::ISize | NumberKind::I128
347        )
348    }
349
350    pub fn is_i64_or_smaller(&self) -> bool {
351        matches!(
352            self.integer,
353            NumberKind::I8
354                | NumberKind::I16
355                | NumberKind::I32
356                | NumberKind::ISize
357                | NumberKind::I64
358                | NumberKind::U8
359                | NumberKind::U16
360                | NumberKind::U32
361        )
362    }
363
364    pub fn is_i64_or_larger(&self) -> bool {
365        matches!(self.integer, NumberKind::I64 | NumberKind::I128)
366    }
367
368    pub fn is_isize_or_smaller(&self) -> bool {
369        matches!(
370            self.integer,
371            NumberKind::I8 | NumberKind::I16 | NumberKind::I32 | NumberKind::ISize | NumberKind::U8
372        )
373    }
374
375    pub fn is_isize_or_larger(&self) -> bool {
376        matches!(
377            self.integer,
378            NumberKind::ISize | NumberKind::I64 | NumberKind::I128
379        )
380    }
381
382    pub fn is_i128_or_smaller(&self) -> bool {
383        matches!(
384            self.integer,
385            NumberKind::I8
386                | NumberKind::I16
387                | NumberKind::I32
388                | NumberKind::I64
389                | NumberKind::I128
390                | NumberKind::U8
391                | NumberKind::U16
392                | NumberKind::U32
393                | NumberKind::U64
394                | NumberKind::USize
395        )
396    }
397}