Skip to main content

ad_astra/exports/
range.rs

1////////////////////////////////////////////////////////////////////////////////
2// This file is part of "Ad Astra", an embeddable scripting programming       //
3// language platform.                                                         //
4//                                                                            //
5// This work is proprietary software with source-available code.              //
6//                                                                            //
7// To copy, use, distribute, or contribute to this work, you must agree to    //
8// the terms of the General License Agreement:                                //
9//                                                                            //
10// https://github.com/Eliah-Lakhin/ad-astra/blob/master/EULA.md               //
11//                                                                            //
12// The agreement grants a Basic Commercial License, allowing you to use       //
13// this work in non-commercial and limited commercial products with a total   //
14// gross revenue cap. To remove this commercial limit for one of your         //
15// products, you must acquire a Full Commercial License.                      //
16//                                                                            //
17// If you contribute to the source code, documentation, or related materials, //
18// you must grant me an exclusive license to these contributions.             //
19// Contributions are governed by the "Contributions" section of the General   //
20// License Agreement.                                                         //
21//                                                                            //
22// Copying the work in parts is strictly forbidden, except as permitted       //
23// under the General License Agreement.                                       //
24//                                                                            //
25// If you do not or cannot agree to the terms of this Agreement,              //
26// do not use this work.                                                      //
27//                                                                            //
28// This work is provided "as is", without any warranties, express or implied, //
29// except where such disclaimers are legally invalid.                         //
30//                                                                            //
31// Copyright (c) 2024 Ilya Lakhin (Илья Александрович Лахин).                 //
32// All rights reserved.                                                       //
33////////////////////////////////////////////////////////////////////////////////
34
35use std::{
36    any::type_name,
37    ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive},
38    ptr::{addr_of, addr_of_mut},
39    sync::Arc,
40};
41
42use crate::{
43    export,
44    exports::utils::transparent_upcast,
45    runtime::{
46        ops::{ScriptClone, ScriptDebug, ScriptHash, ScriptPartialEq},
47        Arg,
48        Cell,
49        Downcast,
50        NumericOperationKind,
51        Origin,
52        Provider,
53        RuntimeError,
54        RuntimeResult,
55        ScriptType,
56        TypeHint,
57        Upcast,
58    },
59};
60
61/// A range of integer numbers: `10..125`.
62///
63/// This range includes all integer numbers starting from the lower value
64/// (inclusive) up to the upper value (exclusive).
65#[export(include)]
66#[export(name "range")]
67pub(crate) type RangeType = Range<usize>;
68
69impl<'a> Downcast<'a> for RangeType {
70    fn downcast(origin: Origin, provider: Provider<'a>) -> RuntimeResult<Self> {
71        let mut type_match = provider.type_match();
72
73        if type_match.is::<Self>() {
74            return provider.to_owned().take(origin);
75        }
76
77        Err(type_match.mismatch(origin))
78    }
79
80    #[inline(always)]
81    fn hint() -> TypeHint {
82        TypeHint::Type(Self::type_meta())
83    }
84}
85
86impl<'a> Downcast<'a> for &'a RangeType {
87    #[inline]
88    fn downcast(origin: Origin, provider: Provider<'a>) -> RuntimeResult<Self> {
89        let mut type_match = provider.type_match();
90
91        if type_match.is::<RangeType>() {
92            return provider.to_borrowed(&origin)?.borrow_ref(origin);
93        }
94
95        Err(type_match.mismatch(origin))
96    }
97
98    #[inline(always)]
99    fn hint() -> TypeHint {
100        TypeHint::Type(RangeType::type_meta())
101    }
102}
103
104impl<'a> Downcast<'a> for &'a mut RangeType {
105    #[inline]
106    fn downcast(origin: Origin, provider: Provider<'a>) -> RuntimeResult<Self> {
107        let mut type_match = provider.type_match();
108
109        if type_match.is::<RangeType>() {
110            return provider.to_borrowed(&origin)?.borrow_mut(origin);
111        }
112
113        Err(type_match.mismatch(origin))
114    }
115
116    #[inline(always)]
117    fn hint() -> TypeHint {
118        TypeHint::Type(RangeType::type_meta())
119    }
120}
121
122transparent_upcast!(RangeType);
123
124#[export(include)]
125impl ScriptClone for RangeType {}
126
127#[export(include)]
128impl ScriptHash for RangeType {}
129
130#[export(include)]
131impl ScriptDebug for RangeType {}
132
133#[export(include)]
134impl ScriptPartialEq for RangeType {
135    type RHS = Self;
136
137    fn script_eq(_origin: Origin, mut lhs: Arg, mut rhs: Arg) -> RuntimeResult<bool> {
138        let lhs = lhs.data.borrow_ref::<Self>(lhs.origin)?;
139
140        let mut type_match = rhs.data.type_match();
141
142        if type_match.is::<Self>() {
143            let rhs = rhs.data.borrow_ref::<Self>(rhs.origin)?;
144
145            return Ok(lhs == rhs);
146        }
147
148        if type_match.belongs_to::<usize>() {
149            let singleton =
150                <usize as Downcast>::downcast(rhs.origin, Provider::Borrowed(&mut rhs.data))?;
151
152            return match singleton == usize::MAX {
153                true => Ok(false),
154
155                false => Ok(lhs.start == singleton && lhs.end == singleton + 1),
156            };
157        }
158
159        Err(type_match.mismatch(rhs.origin))
160    }
161}
162
163trait RangeImpl {
164    fn start(origin: Origin, lhs: Arg) -> RuntimeResult<Cell>;
165    fn end(origin: Origin, lhs: Arg) -> RuntimeResult<Cell>;
166}
167
168#[export(include)]
169impl RangeImpl for RangeType {
170    #[export(component RangeType)]
171    fn start(origin: Origin, lhs: Arg) -> RuntimeResult<Cell> {
172        unsafe fn by_ref(range: *const RangeType) -> *const usize {
173            // Safety: Upheld by the Cell::map_component specification.
174            unsafe { addr_of!((*range).start) }
175        }
176
177        unsafe fn by_mut(range: *mut RangeType) -> *mut usize {
178            // Safety: Upheld by the Cell::map_component specification.
179            unsafe { addr_of_mut!((*range).start) }
180        }
181
182        lhs.data
183            .map_ptr::<Self, usize>(origin, Some(by_ref), Some(by_mut))
184    }
185
186    #[export(component RangeType)]
187    fn end(origin: Origin, lhs: Arg) -> RuntimeResult<Cell> {
188        unsafe fn by_ref(range: *const RangeType) -> *const usize {
189            // Safety: Upheld by the Cell::map_component specification.
190            unsafe { addr_of!((*range).end) }
191        }
192
193        unsafe fn by_mut(range: *mut RangeType) -> *mut usize {
194            // Safety: Upheld by the Cell::map_component specification.
195            unsafe { addr_of_mut!((*range).end) }
196        }
197
198        lhs.data
199            .map_ptr::<Self, usize>(origin, Some(by_ref), Some(by_mut))
200    }
201}
202
203impl<'a> Downcast<'a> for RangeFrom<usize> {
204    #[inline]
205    fn downcast(origin: Origin, provider: Provider<'a>) -> RuntimeResult<Self> {
206        let range = provider.to_owned().take::<RangeType>(origin)?;
207
208        if range.end != usize::MAX {
209            return Err(RuntimeError::RangeCast {
210                access_origin: origin,
211                from: range,
212                to: type_name::<RangeFrom<usize>>(),
213            });
214        }
215
216        Ok(RangeFrom { start: range.start })
217    }
218
219    #[inline(always)]
220    fn hint() -> TypeHint {
221        TypeHint::Type(RangeType::type_meta())
222    }
223}
224
225impl<'a> Upcast<'a> for RangeFrom<usize> {
226    type Output = Box<RangeType>;
227
228    #[inline(always)]
229    fn upcast(_origin: Origin, this: Self) -> RuntimeResult<Self::Output> {
230        Ok(Box::new(this.start..usize::MAX))
231    }
232
233    #[inline(always)]
234    fn hint() -> TypeHint {
235        TypeHint::Type(RangeType::type_meta())
236    }
237}
238
239impl<'a> Downcast<'a> for RangeFull {
240    #[inline]
241    fn downcast(origin: Origin, provider: Provider<'a>) -> RuntimeResult<Self> {
242        let range = provider.to_owned().take::<RangeType>(origin)?;
243
244        if range.start != 0 || range.end != usize::MAX {
245            return Err(RuntimeError::RangeCast {
246                access_origin: origin,
247                from: range,
248                to: type_name::<RangeFrom<usize>>(),
249            });
250        }
251
252        Ok(RangeFull)
253    }
254
255    #[inline(always)]
256    fn hint() -> TypeHint {
257        TypeHint::Type(RangeType::type_meta())
258    }
259}
260
261impl<'a> Upcast<'a> for RangeFull {
262    type Output = Box<RangeType>;
263
264    #[inline(always)]
265    fn upcast(_origin: Origin, _this: Self) -> RuntimeResult<Self::Output> {
266        Ok(Box::new(0..usize::MAX))
267    }
268
269    #[inline(always)]
270    fn hint() -> TypeHint {
271        TypeHint::Type(RangeType::type_meta())
272    }
273}
274
275impl<'a> Downcast<'a> for RangeInclusive<usize> {
276    #[inline]
277    fn downcast(origin: Origin, provider: Provider<'a>) -> RuntimeResult<Self> {
278        let mut type_match = provider.type_match();
279
280        if type_match.is::<RangeType>() {
281            let range = provider.to_owned().take::<RangeType>(origin)?;
282
283            let start = range.start;
284
285            let end = match start.checked_add(1) {
286                Some(bound) => bound,
287
288                None => {
289                    return Err(RuntimeError::NumericOperation {
290                        invoke_origin: origin,
291                        kind: NumericOperationKind::Add,
292                        lhs: (usize::type_meta(), Arc::new(start)),
293                        rhs: Some((usize::type_meta(), Arc::new(1))),
294                        target: usize::type_meta(),
295                    })
296                }
297            };
298
299            return Ok(start..=end);
300        }
301
302        if type_match.belongs_to::<usize>() {
303            let start = <usize as Downcast>::downcast(origin, provider)?;
304
305            return Ok(start..=start);
306        }
307
308        Err(type_match.mismatch(origin))
309    }
310
311    #[inline(always)]
312    fn hint() -> TypeHint {
313        TypeHint::Type(RangeType::type_meta())
314    }
315}
316
317impl<'a> Upcast<'a> for RangeInclusive<usize> {
318    type Output = Box<RangeType>;
319
320    #[inline]
321    fn upcast(origin: Origin, this: Self) -> RuntimeResult<Self::Output> {
322        let start = *this.start();
323
324        let end = match start.checked_add(1) {
325            Some(bound) => bound,
326
327            None => {
328                return Err(RuntimeError::NumericOperation {
329                    invoke_origin: origin,
330                    kind: NumericOperationKind::Add,
331                    lhs: (usize::type_meta(), Arc::new(start)),
332                    rhs: Some((usize::type_meta(), Arc::new(1))),
333                    target: usize::type_meta(),
334                })
335            }
336        };
337
338        Ok(Box::new(start..end))
339    }
340
341    #[inline(always)]
342    fn hint() -> TypeHint {
343        TypeHint::Type(RangeType::type_meta())
344    }
345}
346
347impl<'a> Downcast<'a> for RangeTo<usize> {
348    #[inline]
349    fn downcast(origin: Origin, provider: Provider<'a>) -> RuntimeResult<Self> {
350        let range = provider.to_owned().take::<RangeType>(origin)?;
351
352        if range.start != 0 {
353            return Err(RuntimeError::RangeCast {
354                access_origin: origin,
355                from: range,
356                to: type_name::<RangeTo<usize>>(),
357            });
358        }
359
360        Ok(..range.end)
361    }
362
363    #[inline(always)]
364    fn hint() -> TypeHint {
365        TypeHint::Type(RangeType::type_meta())
366    }
367}
368
369impl<'a> Upcast<'a> for RangeTo<usize> {
370    type Output = Box<RangeType>;
371
372    #[inline(always)]
373    fn upcast(_origin: Origin, this: Self) -> RuntimeResult<Self::Output> {
374        Ok(Box::new(0..this.end))
375    }
376
377    #[inline(always)]
378    fn hint() -> TypeHint {
379        TypeHint::Type(RangeType::type_meta())
380    }
381}
382
383impl<'a> Downcast<'a> for RangeToInclusive<usize> {
384    #[inline]
385    fn downcast(origin: Origin, provider: Provider<'a>) -> RuntimeResult<Self> {
386        let range = provider.to_owned().take::<RangeType>(origin)?;
387
388        if range.start != 0 {
389            return Err(RuntimeError::RangeCast {
390                access_origin: origin,
391                from: range,
392                to: type_name::<RangeToInclusive<usize>>(),
393            });
394        }
395
396        let end = match range.end.checked_add(1) {
397            Some(bound) => bound,
398
399            None => {
400                return Err(RuntimeError::NumericOperation {
401                    invoke_origin: origin,
402                    kind: NumericOperationKind::Add,
403                    lhs: (usize::type_meta(), Arc::new(range.end)),
404                    rhs: Some((usize::type_meta(), Arc::new(1))),
405                    target: usize::type_meta(),
406                })
407            }
408        };
409
410        Ok(..=end)
411    }
412
413    #[inline(always)]
414    fn hint() -> TypeHint {
415        TypeHint::Type(RangeType::type_meta())
416    }
417}