rune/runtime/
range_to.rs

1use core::cmp::Ordering;
2use core::fmt;
3use core::ops;
4
5use crate as rune;
6use crate::alloc::clone::TryClone;
7use crate::Any;
8
9use super::{EnvProtocolCaller, FromValue, ProtocolCaller, RuntimeError, ToValue, Value, VmError};
10
11/// Type for an inclusive range expression `..end`.
12///
13/// # Examples
14///
15/// ```rune
16/// let range = ..10;
17/// assert!(range.contains(-10));
18/// assert!(range.contains(5));
19/// assert!(!range.contains(10));
20/// assert!(!range.contains(20));
21///
22/// assert!(range is std::ops::RangeTo);
23/// ```
24///
25/// Ranges can contain any type:
26///
27/// ```rune
28/// let range = ..'f';
29/// assert_eq!(range.end, 'f');
30/// range.end = 'g';
31/// assert_eq!(range.end, 'g');
32/// ```
33///
34/// # Examples
35///
36/// ```rust
37/// use rune::runtime::RangeTo;
38///
39/// let end = rune::to_value(1)?;
40/// let _ = RangeTo::new(end);
41/// # Ok::<_, rune::support::Error>(())
42/// ```
43#[derive(Any, Clone, TryClone)]
44#[try_clone(crate)]
45#[rune(crate)]
46#[rune(constructor, item = ::std::ops)]
47pub struct RangeTo {
48    /// The end value of the range.
49    #[rune(get, set)]
50    pub end: Value,
51}
52
53impl RangeTo {
54    /// Construct a new range.
55    pub fn new(end: Value) -> Self {
56        Self { end }
57    }
58
59    /// Test the range for partial equality.
60    ///
61    /// # Examples
62    ///
63    /// ```rune
64    /// let range = ..'e';
65    /// assert!(range == (..'e'));
66    /// assert!(range != (..'f'));
67    ///
68    /// let range = ..2.0;
69    /// assert!(range == (..2.0));
70    /// assert!(range != (..f64::NAN));
71    /// assert!((..f64::NAN) != (..f64::NAN));
72    /// ```
73    #[rune::function(keep, protocol = PARTIAL_EQ)]
74    pub fn partial_eq(&self, other: &Self) -> Result<bool, VmError> {
75        self.partial_eq_with(other, &mut EnvProtocolCaller)
76    }
77
78    pub(crate) fn partial_eq_with(
79        &self,
80        b: &Self,
81        caller: &mut dyn ProtocolCaller,
82    ) -> Result<bool, VmError> {
83        Value::partial_eq_with(&self.end, &b.end, caller)
84    }
85
86    /// Test the range for total equality.
87    ///
88    /// # Examples
89    ///
90    /// ```rune
91    /// use std::ops::eq;
92    ///
93    /// let range = ..'e';
94    /// assert!(eq(range, ..'e'));
95    /// assert!(!eq(range, ..'f'));
96    /// ```
97    #[rune::function(keep, protocol = EQ)]
98    pub fn eq(&self, other: &Self) -> Result<bool, VmError> {
99        self.eq_with(other, &mut EnvProtocolCaller)
100    }
101
102    pub(crate) fn eq_with(
103        &self,
104        b: &Self,
105        caller: &mut dyn ProtocolCaller,
106    ) -> Result<bool, VmError> {
107        Value::eq_with(&self.end, &b.end, caller)
108    }
109
110    /// Test the range for partial ordering.
111    ///
112    /// # Examples
113    ///
114    /// ```rune
115    /// assert!((..'a') < (..'b'));
116    /// assert!((..'d') > (..'b'));
117    /// assert!(!((..f64::NAN) > (..f64::INFINITY)));
118    /// assert!(!((..f64::NAN) < (..f64::INFINITY)));
119    /// ```
120    #[rune::function(keep, protocol = PARTIAL_CMP)]
121    pub fn partial_cmp(&self, other: &Self) -> Result<Option<Ordering>, VmError> {
122        self.partial_cmp_with(other, &mut EnvProtocolCaller)
123    }
124
125    pub(crate) fn partial_cmp_with(
126        &self,
127        b: &Self,
128        caller: &mut dyn ProtocolCaller,
129    ) -> Result<Option<Ordering>, VmError> {
130        Value::partial_cmp_with(&self.end, &b.end, caller)
131    }
132
133    /// Test the range for total ordering.
134    ///
135    /// # Examples
136    ///
137    /// ```rune
138    /// use std::ops::cmp;
139    /// use std::cmp::Ordering;
140    ///
141    /// assert_eq!(cmp(..'a', ..'b'), Ordering::Less);
142    /// assert_eq!(cmp(..'c', ..'b'), Ordering::Greater);
143    /// ```
144    #[rune::function(keep, protocol = CMP)]
145    pub fn cmp(&self, other: &Self) -> Result<Ordering, VmError> {
146        self.cmp_with(other, &mut EnvProtocolCaller)
147    }
148
149    pub(crate) fn cmp_with(
150        &self,
151        b: &Self,
152        caller: &mut dyn ProtocolCaller,
153    ) -> Result<Ordering, VmError> {
154        Value::cmp_with(&self.end, &b.end, caller)
155    }
156
157    /// Test if the range contains the given value.
158    ///
159    /// The check is performed using the [`PARTIAL_CMP`] protocol.
160    ///
161    /// # Examples
162    ///
163    /// ```rune
164    /// let range = ..10;
165    ///
166    /// assert!(range.contains(-10));
167    /// assert!(range.contains(5));
168    /// assert!(!range.contains(10));
169    /// assert!(!range.contains(20));
170    ///
171    /// assert!(range is std::ops::RangeTo);
172    /// ```
173    #[rune::function(keep)]
174    pub(crate) fn contains(&self, value: Value) -> Result<bool, VmError> {
175        self.contains_with(value, &mut EnvProtocolCaller)
176    }
177
178    pub(crate) fn contains_with(
179        &self,
180        value: Value,
181        caller: &mut dyn ProtocolCaller,
182    ) -> Result<bool, VmError> {
183        Ok(matches!(
184            Value::partial_cmp_with(&self.end, &value, caller)?,
185            Some(Ordering::Greater)
186        ))
187    }
188}
189
190impl fmt::Debug for RangeTo {
191    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192        write!(f, "..{:?}", self.end)
193    }
194}
195
196impl<Idx> ToValue for ops::RangeTo<Idx>
197where
198    Idx: ToValue,
199{
200    fn to_value(self) -> Result<Value, RuntimeError> {
201        let end = self.end.to_value()?;
202        Ok(Value::new(RangeTo::new(end))?)
203    }
204}
205
206impl<Idx> FromValue for ops::RangeTo<Idx>
207where
208    Idx: FromValue,
209{
210    #[inline]
211    fn from_value(value: Value) -> Result<Self, RuntimeError> {
212        let range = value.downcast::<RangeTo>()?;
213        let end = Idx::from_value(range.end)?;
214        Ok(ops::RangeTo { end })
215    }
216}