1use core::any;
2use core::cmp::Ordering;
3use core::fmt;
4use core::hash::Hash as _;
5
6#[cfg(feature = "musli")]
7use musli::{Decode, Encode};
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11use crate as rune;
12use crate::runtime::{
13 Hasher, OwnedTuple, Protocol, RuntimeError, Type, TypeInfo, VmErrorKind, VmIntegerRepr,
14};
15use crate::{Hash, TypeHash};
16
17#[derive(Clone, Copy)]
19#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
20#[cfg_attr(feature = "musli", derive(Decode, Encode))]
21pub enum Inline {
22 Empty,
30 Unit,
32 Bool(bool),
34 Char(char),
36 Signed(i64),
38 Unsigned(u64),
40 Float(f64),
42 Type(Type),
44 Ordering(
46 #[cfg_attr(feature = "musli", musli(with = crate::musli::ordering))]
47 #[cfg_attr(feature = "serde", serde(with = "crate::serde::ordering"))]
48 Ordering,
49 ),
50 Hash(Hash),
52}
53
54impl Inline {
55 pub(crate) fn as_integer<T>(self) -> Result<T, RuntimeError>
56 where
57 T: TryFrom<u64> + TryFrom<i64>,
58 {
59 match self {
60 Inline::Unsigned(value) => match value.try_into() {
61 Ok(number) => Ok(number),
62 Err(..) => Err(RuntimeError::new(
63 VmErrorKind::ValueToIntegerCoercionError {
64 from: VmIntegerRepr::from(value),
65 to: any::type_name::<T>(),
66 },
67 )),
68 },
69 Inline::Signed(value) => match value.try_into() {
70 Ok(number) => Ok(number),
71 Err(..) => Err(RuntimeError::new(
72 VmErrorKind::ValueToIntegerCoercionError {
73 from: VmIntegerRepr::from(value),
74 to: any::type_name::<T>(),
75 },
76 )),
77 },
78 ref value => Err(RuntimeError::new(VmErrorKind::ExpectedNumber {
79 actual: value.type_info(),
80 })),
81 }
82 }
83
84 pub(crate) fn partial_eq(&self, other: &Self) -> Result<bool, RuntimeError> {
86 match (self, other) {
87 (Inline::Unit, Inline::Unit) => Ok(true),
88 (Inline::Bool(a), Inline::Bool(b)) => Ok(*a == *b),
89 (Inline::Char(a), Inline::Char(b)) => Ok(*a == *b),
90 (Inline::Signed(a), Inline::Signed(b)) => Ok(*a == *b),
91 (Inline::Signed(a), rhs) => Ok(*a == rhs.as_integer::<i64>()?),
92 (Inline::Unsigned(a), Inline::Unsigned(b)) => Ok(*a == *b),
93 (Inline::Unsigned(a), rhs) => Ok(*a == rhs.as_integer::<u64>()?),
94 (Inline::Float(a), Inline::Float(b)) => Ok(*a == *b),
95 (Inline::Type(a), Inline::Type(b)) => Ok(*a == *b),
96 (Inline::Ordering(a), Inline::Ordering(b)) => Ok(*a == *b),
97 (Inline::Hash(a), Inline::Hash(b)) => Ok(*a == *b),
98 (lhs, rhs) => Err(RuntimeError::from(
99 VmErrorKind::UnsupportedBinaryOperation {
100 op: Protocol::PARTIAL_EQ.name,
101 lhs: lhs.type_info(),
102 rhs: rhs.type_info(),
103 },
104 )),
105 }
106 }
107
108 pub(crate) fn eq(&self, other: &Self) -> Result<bool, RuntimeError> {
110 match (self, other) {
111 (Inline::Unit, Inline::Unit) => Ok(true),
112 (Inline::Bool(a), Inline::Bool(b)) => Ok(*a == *b),
113 (Inline::Char(a), Inline::Char(b)) => Ok(*a == *b),
114 (Inline::Unsigned(a), Inline::Unsigned(b)) => Ok(*a == *b),
115 (Inline::Signed(a), Inline::Signed(b)) => Ok(*a == *b),
116 (Inline::Float(a), Inline::Float(b)) => {
117 let Some(ordering) = a.partial_cmp(b) else {
118 return Err(RuntimeError::new(VmErrorKind::IllegalFloatComparison {
119 lhs: *a,
120 rhs: *b,
121 }));
122 };
123
124 Ok(matches!(ordering, Ordering::Equal))
125 }
126 (Inline::Type(a), Inline::Type(b)) => Ok(*a == *b),
127 (Inline::Ordering(a), Inline::Ordering(b)) => Ok(*a == *b),
128 (Inline::Hash(a), Inline::Hash(b)) => Ok(*a == *b),
129 (lhs, rhs) => Err(RuntimeError::new(VmErrorKind::UnsupportedBinaryOperation {
130 op: Protocol::EQ.name,
131 lhs: lhs.type_info(),
132 rhs: rhs.type_info(),
133 })),
134 }
135 }
136
137 pub(crate) fn partial_cmp(&self, other: &Self) -> Result<Option<Ordering>, RuntimeError> {
139 match (self, other) {
140 (Inline::Unit, Inline::Unit) => Ok(Some(Ordering::Equal)),
141 (Inline::Bool(lhs), Inline::Bool(rhs)) => Ok(lhs.partial_cmp(rhs)),
142 (Inline::Char(lhs), Inline::Char(rhs)) => Ok(lhs.partial_cmp(rhs)),
143 (Inline::Unsigned(lhs), Inline::Unsigned(rhs)) => Ok(lhs.partial_cmp(rhs)),
144 (Inline::Unsigned(lhs), rhs) => {
145 let rhs = rhs.as_integer::<u64>()?;
146 Ok(lhs.partial_cmp(&rhs))
147 }
148 (Inline::Signed(lhs), Inline::Signed(rhs)) => Ok(lhs.partial_cmp(rhs)),
149 (Inline::Signed(lhs), rhs) => {
150 let rhs = rhs.as_integer::<i64>()?;
151 Ok(lhs.partial_cmp(&rhs))
152 }
153 (Inline::Float(lhs), Inline::Float(rhs)) => Ok(lhs.partial_cmp(rhs)),
154 (Inline::Type(lhs), Inline::Type(rhs)) => Ok(lhs.partial_cmp(rhs)),
155 (Inline::Ordering(lhs), Inline::Ordering(rhs)) => Ok(lhs.partial_cmp(rhs)),
156 (Inline::Hash(lhs), Inline::Hash(rhs)) => Ok(lhs.partial_cmp(rhs)),
157 (lhs, rhs) => Err(RuntimeError::from(
158 VmErrorKind::UnsupportedBinaryOperation {
159 op: Protocol::PARTIAL_CMP.name,
160 lhs: lhs.type_info(),
161 rhs: rhs.type_info(),
162 },
163 )),
164 }
165 }
166
167 pub(crate) fn cmp(&self, other: &Self) -> Result<Ordering, RuntimeError> {
169 match (self, other) {
170 (Inline::Unit, Inline::Unit) => Ok(Ordering::Equal),
171 (Inline::Bool(a), Inline::Bool(b)) => Ok(a.cmp(b)),
172 (Inline::Char(a), Inline::Char(b)) => Ok(a.cmp(b)),
173 (Inline::Unsigned(a), Inline::Unsigned(b)) => Ok(a.cmp(b)),
174 (Inline::Signed(a), Inline::Signed(b)) => Ok(a.cmp(b)),
175 (Inline::Float(a), Inline::Float(b)) => {
176 let Some(ordering) = a.partial_cmp(b) else {
177 return Err(RuntimeError::new(VmErrorKind::IllegalFloatComparison {
178 lhs: *a,
179 rhs: *b,
180 }));
181 };
182
183 Ok(ordering)
184 }
185 (Inline::Type(a), Inline::Type(b)) => Ok(a.cmp(b)),
186 (Inline::Ordering(a), Inline::Ordering(b)) => Ok(a.cmp(b)),
187 (Inline::Hash(a), Inline::Hash(b)) => Ok(a.cmp(b)),
188 (lhs, rhs) => Err(RuntimeError::new(VmErrorKind::UnsupportedBinaryOperation {
189 op: Protocol::CMP.name,
190 lhs: lhs.type_info(),
191 rhs: rhs.type_info(),
192 })),
193 }
194 }
195
196 pub(crate) fn hash(&self, hasher: &mut Hasher) -> Result<(), RuntimeError> {
198 match self {
199 Inline::Unsigned(value) => {
200 value.hash(hasher);
201 }
202 Inline::Signed(value) => {
203 value.hash(hasher);
204 }
205 Inline::Float(value) => {
209 if value.is_nan() {
210 return Err(RuntimeError::new(VmErrorKind::IllegalFloatOperation {
211 value: *value,
212 }));
213 }
214
215 let zero = *value == 0.0;
216 let value = ((zero as u8 as f64) * 0.0 + (!zero as u8 as f64) * *value).to_bits();
217 value.hash(hasher);
218 }
219 operand => {
220 return Err(RuntimeError::new(VmErrorKind::UnsupportedUnaryOperation {
221 op: Protocol::HASH.name,
222 operand: operand.type_info(),
223 }));
224 }
225 }
226
227 Ok(())
228 }
229}
230
231impl fmt::Debug for Inline {
232 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233 match *self {
234 Inline::Empty => write!(f, "<empty>"),
235 Inline::Unit => write!(f, "()"),
236 Inline::Bool(value) => value.fmt(f),
237 Inline::Char(value) => value.fmt(f),
238 Inline::Unsigned(value) => value.fmt(f),
239 Inline::Signed(value) => value.fmt(f),
240 Inline::Float(value) => value.fmt(f),
241 Inline::Type(value) => value.fmt(f),
242 Inline::Ordering(value) => value.fmt(f),
243 Inline::Hash(value) => value.fmt(f),
244 }
245 }
246}
247
248impl Inline {
249 pub(crate) fn type_info(&self) -> TypeInfo {
250 match self {
251 Inline::Empty => TypeInfo::empty(),
252 Inline::Unit => TypeInfo::any::<OwnedTuple>(),
253 Inline::Bool(..) => TypeInfo::named::<bool>(),
254 Inline::Char(..) => TypeInfo::named::<char>(),
255 Inline::Unsigned(..) => TypeInfo::named::<u64>(),
256 Inline::Signed(..) => TypeInfo::named::<i64>(),
257 Inline::Float(..) => TypeInfo::named::<f64>(),
258 Inline::Type(..) => TypeInfo::named::<Type>(),
259 Inline::Ordering(..) => TypeInfo::named::<Ordering>(),
260 Inline::Hash(..) => TypeInfo::named::<Hash>(),
261 }
262 }
263
264 pub(crate) fn type_hash(&self) -> Hash {
269 match self {
270 Inline::Empty => crate::hash!(::std::empty::Empty),
271 Inline::Unit => OwnedTuple::HASH,
272 Inline::Bool(..) => bool::HASH,
273 Inline::Char(..) => char::HASH,
274 Inline::Signed(..) => i64::HASH,
275 Inline::Unsigned(..) => u64::HASH,
276 Inline::Float(..) => f64::HASH,
277 Inline::Type(..) => Type::HASH,
278 Inline::Ordering(..) => Ordering::HASH,
279 Inline::Hash(..) => Hash::HASH,
280 }
281 }
282}