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