checked_rs_macro_impl/params/
number_value_range.rs

1use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};
2
3use proc_macro2::{Span, TokenStream};
4use quote::{quote, ToTokens};
5
6use super::{NumberArg, NumberArgRange, NumberKind, NumberValue};
7
8#[derive(Debug, Clone)]
9pub enum NumberValueRange {
10    Full(NumberKind),
11    From(RangeFrom<NumberValue>),
12    ToExclusive(RangeTo<NumberValue>),
13    ToInclusive(RangeToInclusive<NumberValue>),
14    Exclusive(Range<NumberValue>),
15    Inclusive(RangeInclusive<NumberValue>),
16}
17
18impl From<NumberKind> for NumberValueRange {
19    fn from(kind: NumberKind) -> Self {
20        Self::Full(kind)
21    }
22}
23
24impl From<&NumberKind> for NumberValueRange {
25    fn from(kind: &NumberKind) -> Self {
26        Self::Full(*kind)
27    }
28}
29
30impl From<RangeFrom<NumberValue>> for NumberValueRange {
31    fn from(range: RangeFrom<NumberValue>) -> Self {
32        Self::From(range)
33    }
34}
35
36impl From<&RangeFrom<NumberValue>> for NumberValueRange {
37    fn from(range: &RangeFrom<NumberValue>) -> Self {
38        Self::From(range.clone())
39    }
40}
41
42impl From<RangeTo<NumberValue>> for NumberValueRange {
43    fn from(range: RangeTo<NumberValue>) -> Self {
44        Self::ToExclusive(range)
45    }
46}
47
48impl From<&RangeTo<NumberValue>> for NumberValueRange {
49    fn from(range: &RangeTo<NumberValue>) -> Self {
50        Self::ToExclusive(range.clone())
51    }
52}
53
54impl From<RangeToInclusive<NumberValue>> for NumberValueRange {
55    fn from(range: RangeToInclusive<NumberValue>) -> Self {
56        Self::ToInclusive(range)
57    }
58}
59
60impl From<&RangeToInclusive<NumberValue>> for NumberValueRange {
61    fn from(range: &RangeToInclusive<NumberValue>) -> Self {
62        Self::ToInclusive(range.clone())
63    }
64}
65
66impl From<Range<NumberValue>> for NumberValueRange {
67    fn from(range: Range<NumberValue>) -> Self {
68        Self::Exclusive(range)
69    }
70}
71
72impl From<&Range<NumberValue>> for NumberValueRange {
73    fn from(range: &Range<NumberValue>) -> Self {
74        Self::Exclusive(range.clone())
75    }
76}
77
78impl From<RangeInclusive<NumberValue>> for NumberValueRange {
79    fn from(range: RangeInclusive<NumberValue>) -> Self {
80        Self::Inclusive(range)
81    }
82}
83
84impl From<&RangeInclusive<NumberValue>> for NumberValueRange {
85    fn from(range: &RangeInclusive<NumberValue>) -> Self {
86        Self::Inclusive(range.clone())
87    }
88}
89
90impl ToTokens for NumberValueRange {
91    fn to_tokens(&self, tokens: &mut TokenStream) {
92        let min = self.first_val();
93        let max = self.last_val();
94
95        tokens.extend(quote! {
96            #min..=#max
97        });
98    }
99}
100
101impl NumberValueRange {
102    fn check_matching_kinds(
103        a: impl Into<NumberKind> + std::fmt::Debug + Clone,
104        b: impl Into<NumberKind> + std::fmt::Debug + Clone,
105    ) -> syn::Result<()> {
106        let a_kind: NumberKind = a.clone().into();
107        let b_kind: NumberKind = b.clone().into();
108
109        if a_kind != b_kind {
110            return Err(syn::Error::new(
111                Span::call_site(),
112                format!("Number kinds do not match: {:?} != {:?}", a, b),
113            ));
114        }
115
116        Ok(())
117    }
118
119    pub fn kind(&self) -> NumberKind {
120        match self {
121            Self::Full(kind) => *kind,
122            Self::From(range) => range.start.kind(),
123            Self::ToExclusive(range) => range.end.kind(),
124            Self::ToInclusive(range) => range.end.kind(),
125            Self::Exclusive(range) => range.start.kind(),
126            Self::Inclusive(range) => range.start().kind(),
127        }
128    }
129
130    pub fn first_val(&self) -> NumberValue {
131        match self {
132            Self::From(range) => range.start,
133            Self::Inclusive(range) => *range.start(),
134            Self::Exclusive(range) => range.start,
135            _ => {
136                let kind = self.kind();
137                NumberArg::new_min_constant(kind).into_value(kind)
138            }
139        }
140    }
141
142    pub fn last_val(&self) -> NumberValue {
143        match self {
144            Self::ToExclusive(range) => range.end.sub_usize(1),
145            Self::ToInclusive(range) => range.end,
146            Self::Exclusive(range) => range.end.sub_usize(1),
147            Self::Inclusive(range) => *range.end(),
148            _ => {
149                let kind = self.kind();
150                NumberArg::new_max_constant(kind).into_value(kind)
151            }
152        }
153    }
154
155    pub fn contains(&self, val: &NumberValue) -> bool {
156        if *val >= self.first_val() && *val <= self.first_val() {
157            true
158        } else {
159            false
160        }
161    }
162
163    #[must_use]
164    pub fn new_inclusive(
165        start: Option<NumberValue>,
166        end: Option<NumberValue>,
167        kind: NumberKind,
168    ) -> syn::Result<Self> {
169        Ok(match (start, end) {
170            (Some(start), Some(end)) => {
171                Self::check_matching_kinds(start, kind)?;
172                Self::check_matching_kinds(end, kind)?;
173                Self::Inclusive(start..=end)
174            }
175            (Some(start), None) => {
176                Self::check_matching_kinds(start, kind)?;
177                Self::From(start..)
178            }
179            (None, Some(end)) => {
180                Self::check_matching_kinds(end, kind)?;
181                Self::ToInclusive(..=end)
182            }
183            (None, None) => Self::Full(kind),
184        })
185    }
186
187    #[must_use]
188    pub fn new_exclusive(
189        start: Option<NumberValue>,
190        end: Option<NumberValue>,
191        kind: NumberKind,
192    ) -> syn::Result<Self> {
193        Ok(match (start, end) {
194            (Some(start), Some(end)) => {
195                Self::check_matching_kinds(start, kind)?;
196                Self::check_matching_kinds(end, kind)?;
197                Self::Exclusive(start..end)
198            }
199            (Some(start), None) => {
200                Self::check_matching_kinds(start, kind)?;
201                Self::From(start..)
202            }
203            (None, Some(end)) => {
204                Self::check_matching_kinds(end, kind)?;
205                Self::ToExclusive(..end)
206            }
207            (None, None) => Self::Full(kind),
208        })
209    }
210
211    #[must_use]
212    pub fn from_arg_range(arg_range: NumberArgRange, kind: NumberKind) -> syn::Result<Self> {
213        let NumberArgRange {
214            start,
215            end,
216            dot_dot_eq,
217            ..
218        } = arg_range;
219
220        let inclusive = dot_dot_eq.is_some();
221        let start = start.map(|arg| arg.into_value(kind));
222        let end = end.map(|arg| arg.into_value(kind));
223
224        Ok(match (start, end) {
225            (None, None) => Self::Full(kind),
226            (Some(start), None) => {
227                Self::check_matching_kinds(kind, &start)?;
228                Self::From(start..)
229            }
230            (Some(start), Some(end)) => {
231                Self::check_matching_kinds(kind, &start)?;
232                Self::check_matching_kinds(kind, &end)?;
233
234                if inclusive {
235                    Self::Inclusive(start..=end)
236                } else {
237                    Self::Exclusive(start..end)
238                }
239            }
240            (None, Some(end)) => {
241                Self::check_matching_kinds(kind, &end)?;
242
243                if inclusive {
244                    Self::ToInclusive(..=end)
245                } else {
246                    Self::ToExclusive(..end)
247                }
248            }
249        })
250    }
251}