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