1#[macro_use]
2mod macros;
3
4use core::any;
5use core::cmp::Ordering;
6use core::fmt;
7
8use rust_alloc::sync::Arc;
9
10use serde::{Deserialize, Serialize};
11
12use crate as rune;
13use crate::alloc::prelude::*;
14use crate::alloc::{self, HashMap};
15use crate::runtime;
16use crate::{Hash, TypeHash};
17
18use super::{
19 Bytes, FromValue, Inline, Object, OwnedTuple, Repr, ToValue, Tuple, Type, TypeInfo, Value,
20 VmErrorKind,
21};
22
23pub use rune_macros::ToConstValue;
25
26use super::{AnyTypeInfo, RuntimeError, VmIntegerRepr};
27
28pub trait IntoConstValue {
30 #[doc(hidden)]
32 fn into_const_value(self) -> alloc::Result<ConstValue>;
33}
34
35impl IntoConstValue for ConstValue {
36 #[inline]
37 fn into_const_value(self) -> alloc::Result<ConstValue> {
38 Ok(self)
39 }
40}
41
42impl IntoConstValue for &ConstValue {
43 #[inline]
44 fn into_const_value(self) -> alloc::Result<ConstValue> {
45 self.try_clone()
46 }
47}
48
49pub fn from_const_value<T>(value: impl IntoConstValue) -> Result<T, RuntimeError>
62where
63 T: FromConstValue,
64{
65 T::from_const_value(value.into_const_value()?)
66}
67
68pub fn to_const_value(value: impl ToConstValue) -> Result<ConstValue, RuntimeError> {
81 value.to_const_value()
82}
83
84pub trait ToConstValue: Sized {
86 fn to_const_value(self) -> Result<ConstValue, RuntimeError>;
88
89 #[inline]
91 fn construct() -> Option<Arc<dyn ConstConstruct>> {
92 None
93 }
94}
95
96impl ToConstValue for ConstValue {
97 #[inline]
98 fn to_const_value(self) -> Result<ConstValue, RuntimeError> {
99 Ok(self)
100 }
101}
102
103impl ToConstValue for Value {
104 #[inline]
105 fn to_const_value(self) -> Result<ConstValue, RuntimeError> {
106 ConstValue::from_value_ref(&self)
107 }
108}
109
110#[derive(Debug, TryClone, Deserialize, Serialize)]
111pub(crate) enum ConstValueKind {
112 Inline(#[try_clone(copy)] Inline),
114 String(String),
116 Bytes(Bytes),
118 Vec(Vec<ConstValue>),
120 Tuple(Box<[ConstValue]>),
122 Object(HashMap<String, ConstValue>),
124 Option(Option<Box<ConstValue>>),
126 Struct(Hash, Box<[ConstValue]>),
128}
129
130impl ConstValueKind {
131 fn type_info(&self) -> TypeInfo {
132 fn full_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 write!(f, "constant struct")
134 }
135
136 match self {
137 ConstValueKind::Inline(value) => value.type_info(),
138 ConstValueKind::String(..) => TypeInfo::any::<String>(),
139 ConstValueKind::Bytes(..) => TypeInfo::any::<Bytes>(),
140 ConstValueKind::Vec(..) => TypeInfo::any::<runtime::Vec>(),
141 ConstValueKind::Tuple(..) => TypeInfo::any::<OwnedTuple>(),
142 ConstValueKind::Object(..) => TypeInfo::any::<Object>(),
143 ConstValueKind::Option(..) => TypeInfo::any::<Option<Value>>(),
144 ConstValueKind::Struct(hash, ..) => {
145 TypeInfo::any_type_info(AnyTypeInfo::new(full_name, *hash))
146 }
147 }
148 }
149}
150
151#[derive(Deserialize, Serialize)]
153#[serde(transparent)]
154pub struct ConstValue {
155 kind: ConstValueKind,
156}
157
158impl ConstValue {
159 pub fn tuple(values: Box<[ConstValue]>) -> ConstValue {
161 ConstValue {
162 kind: ConstValueKind::Tuple(values),
163 }
164 }
165
166 pub fn for_struct<const N: usize>(
168 hash: Hash,
169 fields: [ConstValue; N],
170 ) -> Result<ConstValue, RuntimeError> {
171 let fields = Box::<[ConstValue]>::try_from(fields)?;
172
173 Ok(ConstValue {
174 kind: ConstValueKind::Struct(hash, fields),
175 })
176 }
177
178 pub fn as_integer<T>(&self) -> Result<T, RuntimeError>
191 where
192 T: TryFrom<i64> + TryFrom<u64>,
193 {
194 match self.kind {
195 ConstValueKind::Inline(Inline::Signed(value)) => match value.try_into() {
196 Ok(number) => Ok(number),
197 Err(..) => Err(RuntimeError::new(
198 VmErrorKind::ValueToIntegerCoercionError {
199 from: VmIntegerRepr::from(value),
200 to: any::type_name::<T>(),
201 },
202 )),
203 },
204 ConstValueKind::Inline(Inline::Unsigned(value)) => match value.try_into() {
205 Ok(number) => Ok(number),
206 Err(..) => Err(RuntimeError::new(
207 VmErrorKind::ValueToIntegerCoercionError {
208 from: VmIntegerRepr::from(value),
209 to: any::type_name::<T>(),
210 },
211 )),
212 },
213 ref kind => Err(RuntimeError::new(VmErrorKind::ExpectedNumber {
214 actual: kind.type_info(),
215 })),
216 }
217 }
218
219 inline_macros!(inline_into);
220
221 #[inline]
223 pub fn into_tuple(self) -> Result<Box<[ConstValue]>, RuntimeError> {
224 match self.kind {
225 ConstValueKind::Tuple(tuple) => Ok(tuple),
226 kind => Err(RuntimeError::expected::<Tuple>(kind.type_info())),
227 }
228 }
229
230 pub(crate) fn as_kind(&self) -> &ConstValueKind {
232 &self.kind
233 }
234
235 pub(crate) fn from_value_ref(value: &Value) -> Result<ConstValue, RuntimeError> {
237 let inner = match value.as_ref() {
238 Repr::Inline(value) => ConstValueKind::Inline(*value),
239 Repr::Dynamic(value) => {
240 return Err(RuntimeError::from(VmErrorKind::ConstNotSupported {
241 actual: value.type_info(),
242 }));
243 }
244 Repr::Any(value) => match value.type_hash() {
245 Option::<Value>::HASH => {
246 let option = value.borrow_ref::<Option<Value>>()?;
247
248 ConstValueKind::Option(match &*option {
249 Some(some) => Some(Box::try_new(Self::from_value_ref(some)?)?),
250 None => None,
251 })
252 }
253 String::HASH => {
254 let s = value.borrow_ref::<String>()?;
255 ConstValueKind::String(s.try_to_owned()?)
256 }
257 Bytes::HASH => {
258 let s = value.borrow_ref::<Bytes>()?;
259 ConstValueKind::Bytes(s.try_to_owned()?)
260 }
261 runtime::Vec::HASH => {
262 let vec = value.borrow_ref::<runtime::Vec>()?;
263 let mut const_vec = Vec::try_with_capacity(vec.len())?;
264
265 for value in vec.iter() {
266 const_vec.try_push(Self::from_value_ref(value)?)?;
267 }
268
269 ConstValueKind::Vec(const_vec)
270 }
271 runtime::OwnedTuple::HASH => {
272 let tuple = value.borrow_ref::<runtime::OwnedTuple>()?;
273 let mut const_tuple = Vec::try_with_capacity(tuple.len())?;
274
275 for value in tuple.iter() {
276 const_tuple.try_push(Self::from_value_ref(value)?)?;
277 }
278
279 ConstValueKind::Tuple(const_tuple.try_into_boxed_slice()?)
280 }
281 Object::HASH => {
282 let object = value.borrow_ref::<Object>()?;
283 let mut const_object = HashMap::try_with_capacity(object.len())?;
284
285 for (key, value) in object.iter() {
286 let key = key.try_clone()?;
287 let value = Self::from_value_ref(value)?;
288 const_object.try_insert(key, value)?;
289 }
290
291 ConstValueKind::Object(const_object)
292 }
293 _ => {
294 return Err(RuntimeError::from(VmErrorKind::ConstNotSupported {
295 actual: value.type_info(),
296 }));
297 }
298 },
299 };
300
301 Ok(Self { kind: inner })
302 }
303
304 #[inline]
305 #[cfg(test)]
306 pub(crate) fn to_value(&self) -> Result<Value, RuntimeError> {
307 self.to_value_with(&EmptyConstContext)
308 }
309
310 pub(crate) fn to_value_with(&self, cx: &dyn ConstContext) -> Result<Value, RuntimeError> {
316 match &self.kind {
317 ConstValueKind::Inline(value) => Ok(Value::from(*value)),
318 ConstValueKind::String(string) => Ok(Value::try_from(string.try_clone()?)?),
319 ConstValueKind::Bytes(b) => Ok(Value::try_from(b.try_clone()?)?),
320 ConstValueKind::Option(option) => Ok(Value::try_from(match option {
321 Some(some) => Some(Self::to_value_with(some, cx)?),
322 None => None,
323 })?),
324 ConstValueKind::Vec(vec) => {
325 let mut v = runtime::Vec::with_capacity(vec.len())?;
326
327 for value in vec {
328 v.push(Self::to_value_with(value, cx)?)?;
329 }
330
331 Ok(Value::try_from(v)?)
332 }
333 ConstValueKind::Tuple(tuple) => {
334 let mut t = Vec::try_with_capacity(tuple.len())?;
335
336 for value in tuple.iter() {
337 t.try_push(Self::to_value_with(value, cx)?)?;
338 }
339
340 Ok(Value::try_from(OwnedTuple::try_from(t)?)?)
341 }
342 ConstValueKind::Object(object) => {
343 let mut o = Object::with_capacity(object.len())?;
344
345 for (key, value) in object {
346 let key = key.try_clone()?;
347 let value = Self::to_value_with(value, cx)?;
348 o.insert(key, value)?;
349 }
350
351 Ok(Value::try_from(o)?)
352 }
353 ConstValueKind::Struct(hash, fields) => {
354 let Some(constructor) = cx.get(*hash) else {
355 return Err(RuntimeError::missing_constant_constructor(*hash));
356 };
357
358 constructor.const_construct(fields)
359 }
360 }
361 }
362
363 pub(crate) fn type_info(&self) -> TypeInfo {
365 self.kind.type_info()
366 }
367}
368
369impl TryClone for ConstValue {
370 fn try_clone(&self) -> alloc::Result<Self> {
371 Ok(Self {
372 kind: self.kind.try_clone()?,
373 })
374 }
375}
376
377impl FromValue for ConstValue {
378 #[inline]
379 fn from_value(value: Value) -> Result<Self, RuntimeError> {
380 ConstValue::from_value_ref(&value)
381 }
382}
383
384impl ToValue for ConstValue {
385 #[inline]
386 fn to_value(self) -> Result<Value, RuntimeError> {
387 ConstValue::to_value_with(&self, &EmptyConstContext)
388 }
389}
390
391impl From<ConstValueKind> for ConstValue {
392 #[inline]
393 fn from(kind: ConstValueKind) -> Self {
394 Self { kind }
395 }
396}
397
398impl From<Inline> for ConstValue {
399 #[inline]
400 fn from(value: Inline) -> Self {
401 Self::from(ConstValueKind::Inline(value))
402 }
403}
404
405impl From<String> for ConstValue {
406 #[inline]
407 fn from(value: String) -> Self {
408 Self::from(ConstValueKind::String(value))
409 }
410}
411
412impl From<Bytes> for ConstValue {
413 #[inline]
414 fn from(value: Bytes) -> Self {
415 Self::from(ConstValueKind::Bytes(value))
416 }
417}
418
419impl TryFrom<&str> for ConstValue {
420 type Error = alloc::Error;
421
422 #[inline]
423 fn try_from(value: &str) -> Result<Self, Self::Error> {
424 Ok(ConstValue::from(String::try_from(value)?))
425 }
426}
427
428impl ToConstValue for &str {
429 #[inline]
430 fn to_const_value(self) -> Result<ConstValue, RuntimeError> {
431 Ok(ConstValue::try_from(self)?)
432 }
433}
434
435impl TryFrom<&[u8]> for ConstValue {
436 type Error = alloc::Error;
437
438 #[inline]
439 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
440 Ok(ConstValue::from(Bytes::try_from(value)?))
441 }
442}
443
444impl ToConstValue for &[u8] {
445 #[inline]
446 fn to_const_value(self) -> Result<ConstValue, RuntimeError> {
447 Ok(ConstValue::try_from(self)?)
448 }
449}
450
451impl fmt::Debug for ConstValue {
452 #[inline]
453 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
454 self.kind.fmt(f)
455 }
456}
457
458pub trait FromConstValue: Sized {
460 fn from_const_value(value: ConstValue) -> Result<Self, RuntimeError>;
462}
463
464impl FromConstValue for ConstValue {
465 #[inline]
466 fn from_const_value(value: ConstValue) -> Result<Self, RuntimeError> {
467 Ok(value)
468 }
469}
470
471pub trait ConstConstruct: 'static + Send + Sync {
476 #[doc(hidden)]
478 fn const_construct(&self, fields: &[ConstValue]) -> Result<Value, RuntimeError>;
479
480 #[doc(hidden)]
482 fn runtime_construct(&self, fields: &mut [Value]) -> Result<Value, RuntimeError>;
483}
484
485pub(crate) trait ConstContext {
486 fn get(&self, hash: Hash) -> Option<&dyn ConstConstruct>;
487}
488
489pub(crate) struct EmptyConstContext;
490
491impl ConstContext for EmptyConstContext {
492 #[inline]
493 fn get(&self, _: Hash) -> Option<&dyn ConstConstruct> {
494 None
495 }
496}