#[macro_use]
mod macros;
use core::any;
use core::cmp::Ordering;
use core::fmt;
use rust_alloc::sync::Arc;
use serde::{Deserialize, Serialize};
use crate as rune;
use crate::alloc::prelude::*;
use crate::alloc::{self, HashMap};
use crate::runtime;
use crate::{Hash, TypeHash};
use super::{
Bytes, FromValue, Inline, Object, OwnedTuple, Repr, ToValue, Tuple, Type, TypeInfo, Value,
VmErrorKind,
};
pub use rune_macros::ToConstValue;
use super::{AnyTypeInfo, RuntimeError, VmIntegerRepr};
pub trait IntoConstValue {
#[doc(hidden)]
fn into_const_value(self) -> alloc::Result<ConstValue>;
}
impl IntoConstValue for ConstValue {
#[inline]
fn into_const_value(self) -> alloc::Result<ConstValue> {
Ok(self)
}
}
impl IntoConstValue for &ConstValue {
#[inline]
fn into_const_value(self) -> alloc::Result<ConstValue> {
self.try_clone()
}
}
pub fn from_const_value<T>(value: impl IntoConstValue) -> Result<T, RuntimeError>
where
T: FromConstValue,
{
T::from_const_value(value.into_const_value()?)
}
pub fn to_const_value(value: impl ToConstValue) -> Result<ConstValue, RuntimeError> {
value.to_const_value()
}
pub trait ToConstValue: Sized {
fn to_const_value(self) -> Result<ConstValue, RuntimeError>;
#[inline]
fn construct() -> Option<Arc<dyn ConstConstruct>> {
None
}
}
impl ToConstValue for ConstValue {
#[inline]
fn to_const_value(self) -> Result<ConstValue, RuntimeError> {
Ok(self)
}
}
impl ToConstValue for Value {
#[inline]
fn to_const_value(self) -> Result<ConstValue, RuntimeError> {
ConstValue::from_value_ref(&self)
}
}
#[derive(Debug, TryClone, Deserialize, Serialize)]
pub(crate) enum ConstValueKind {
Inline(#[try_clone(copy)] Inline),
String(String),
Bytes(Bytes),
Vec(Vec<ConstValue>),
Tuple(Box<[ConstValue]>),
Object(HashMap<String, ConstValue>),
Option(Option<Box<ConstValue>>),
Struct(Hash, Box<[ConstValue]>),
}
impl ConstValueKind {
fn type_info(&self) -> TypeInfo {
fn full_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "constant struct")
}
match self {
ConstValueKind::Inline(value) => value.type_info(),
ConstValueKind::String(..) => TypeInfo::any::<String>(),
ConstValueKind::Bytes(..) => TypeInfo::any::<Bytes>(),
ConstValueKind::Vec(..) => TypeInfo::any::<runtime::Vec>(),
ConstValueKind::Tuple(..) => TypeInfo::any::<OwnedTuple>(),
ConstValueKind::Object(..) => TypeInfo::any::<Object>(),
ConstValueKind::Option(..) => TypeInfo::any::<Option<Value>>(),
ConstValueKind::Struct(hash, ..) => {
TypeInfo::any_type_info(AnyTypeInfo::new(full_name, *hash))
}
}
}
}
#[derive(Deserialize, Serialize)]
#[serde(transparent)]
pub struct ConstValue {
kind: ConstValueKind,
}
impl ConstValue {
pub fn tuple(values: Box<[ConstValue]>) -> ConstValue {
ConstValue {
kind: ConstValueKind::Tuple(values),
}
}
pub fn for_struct<const N: usize>(
hash: Hash,
fields: [ConstValue; N],
) -> Result<ConstValue, RuntimeError> {
let fields = Box::<[ConstValue]>::try_from(fields)?;
Ok(ConstValue {
kind: ConstValueKind::Struct(hash, fields),
})
}
pub fn as_integer<T>(&self) -> Result<T, RuntimeError>
where
T: TryFrom<i64> + TryFrom<u64>,
{
match self.kind {
ConstValueKind::Inline(Inline::Signed(value)) => match value.try_into() {
Ok(number) => Ok(number),
Err(..) => Err(RuntimeError::new(
VmErrorKind::ValueToIntegerCoercionError {
from: VmIntegerRepr::from(value),
to: any::type_name::<T>(),
},
)),
},
ConstValueKind::Inline(Inline::Unsigned(value)) => match value.try_into() {
Ok(number) => Ok(number),
Err(..) => Err(RuntimeError::new(
VmErrorKind::ValueToIntegerCoercionError {
from: VmIntegerRepr::from(value),
to: any::type_name::<T>(),
},
)),
},
ref kind => Err(RuntimeError::new(VmErrorKind::ExpectedNumber {
actual: kind.type_info(),
})),
}
}
inline_macros!(inline_into);
#[inline]
pub fn into_tuple(self) -> Result<Box<[ConstValue]>, RuntimeError> {
match self.kind {
ConstValueKind::Tuple(tuple) => Ok(tuple),
kind => Err(RuntimeError::expected::<Tuple>(kind.type_info())),
}
}
pub(crate) fn as_kind(&self) -> &ConstValueKind {
&self.kind
}
pub(crate) fn from_value_ref(value: &Value) -> Result<ConstValue, RuntimeError> {
let inner = match value.as_ref() {
Repr::Inline(value) => ConstValueKind::Inline(*value),
Repr::Dynamic(value) => {
return Err(RuntimeError::from(VmErrorKind::ConstNotSupported {
actual: value.type_info(),
}));
}
Repr::Any(value) => match value.type_hash() {
Option::<Value>::HASH => {
let option = value.borrow_ref::<Option<Value>>()?;
ConstValueKind::Option(match &*option {
Some(some) => Some(Box::try_new(Self::from_value_ref(some)?)?),
None => None,
})
}
String::HASH => {
let s = value.borrow_ref::<String>()?;
ConstValueKind::String(s.try_to_owned()?)
}
Bytes::HASH => {
let s = value.borrow_ref::<Bytes>()?;
ConstValueKind::Bytes(s.try_to_owned()?)
}
runtime::Vec::HASH => {
let vec = value.borrow_ref::<runtime::Vec>()?;
let mut const_vec = Vec::try_with_capacity(vec.len())?;
for value in vec.iter() {
const_vec.try_push(Self::from_value_ref(value)?)?;
}
ConstValueKind::Vec(const_vec)
}
runtime::OwnedTuple::HASH => {
let tuple = value.borrow_ref::<runtime::OwnedTuple>()?;
let mut const_tuple = Vec::try_with_capacity(tuple.len())?;
for value in tuple.iter() {
const_tuple.try_push(Self::from_value_ref(value)?)?;
}
ConstValueKind::Tuple(const_tuple.try_into_boxed_slice()?)
}
Object::HASH => {
let object = value.borrow_ref::<Object>()?;
let mut const_object = HashMap::try_with_capacity(object.len())?;
for (key, value) in object.iter() {
let key = key.try_clone()?;
let value = Self::from_value_ref(value)?;
const_object.try_insert(key, value)?;
}
ConstValueKind::Object(const_object)
}
_ => {
return Err(RuntimeError::from(VmErrorKind::ConstNotSupported {
actual: value.type_info(),
}));
}
},
};
Ok(Self { kind: inner })
}
#[inline]
#[cfg(test)]
pub(crate) fn to_value(&self) -> Result<Value, RuntimeError> {
self.to_value_with(&EmptyConstContext)
}
pub(crate) fn to_value_with(&self, cx: &dyn ConstContext) -> Result<Value, RuntimeError> {
match &self.kind {
ConstValueKind::Inline(value) => Ok(Value::from(*value)),
ConstValueKind::String(string) => Ok(Value::try_from(string.try_clone()?)?),
ConstValueKind::Bytes(b) => Ok(Value::try_from(b.try_clone()?)?),
ConstValueKind::Option(option) => Ok(Value::try_from(match option {
Some(some) => Some(Self::to_value_with(some, cx)?),
None => None,
})?),
ConstValueKind::Vec(vec) => {
let mut v = runtime::Vec::with_capacity(vec.len())?;
for value in vec {
v.push(Self::to_value_with(value, cx)?)?;
}
Ok(Value::try_from(v)?)
}
ConstValueKind::Tuple(tuple) => {
let mut t = Vec::try_with_capacity(tuple.len())?;
for value in tuple.iter() {
t.try_push(Self::to_value_with(value, cx)?)?;
}
Ok(Value::try_from(OwnedTuple::try_from(t)?)?)
}
ConstValueKind::Object(object) => {
let mut o = Object::with_capacity(object.len())?;
for (key, value) in object {
let key = key.try_clone()?;
let value = Self::to_value_with(value, cx)?;
o.insert(key, value)?;
}
Ok(Value::try_from(o)?)
}
ConstValueKind::Struct(hash, fields) => {
let Some(constructor) = cx.get(*hash) else {
return Err(RuntimeError::missing_constant_constructor(*hash));
};
constructor.const_construct(fields)
}
}
}
pub(crate) fn type_info(&self) -> TypeInfo {
self.kind.type_info()
}
}
impl TryClone for ConstValue {
fn try_clone(&self) -> alloc::Result<Self> {
Ok(Self {
kind: self.kind.try_clone()?,
})
}
}
impl FromValue for ConstValue {
#[inline]
fn from_value(value: Value) -> Result<Self, RuntimeError> {
ConstValue::from_value_ref(&value)
}
}
impl ToValue for ConstValue {
#[inline]
fn to_value(self) -> Result<Value, RuntimeError> {
ConstValue::to_value_with(&self, &EmptyConstContext)
}
}
impl From<ConstValueKind> for ConstValue {
#[inline]
fn from(kind: ConstValueKind) -> Self {
Self { kind }
}
}
impl From<Inline> for ConstValue {
#[inline]
fn from(value: Inline) -> Self {
Self::from(ConstValueKind::Inline(value))
}
}
impl From<String> for ConstValue {
#[inline]
fn from(value: String) -> Self {
Self::from(ConstValueKind::String(value))
}
}
impl From<Bytes> for ConstValue {
#[inline]
fn from(value: Bytes) -> Self {
Self::from(ConstValueKind::Bytes(value))
}
}
impl TryFrom<&str> for ConstValue {
type Error = alloc::Error;
#[inline]
fn try_from(value: &str) -> Result<Self, Self::Error> {
Ok(ConstValue::from(String::try_from(value)?))
}
}
impl ToConstValue for &str {
#[inline]
fn to_const_value(self) -> Result<ConstValue, RuntimeError> {
Ok(ConstValue::try_from(self)?)
}
}
impl TryFrom<&[u8]> for ConstValue {
type Error = alloc::Error;
#[inline]
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
Ok(ConstValue::from(Bytes::try_from(value)?))
}
}
impl ToConstValue for &[u8] {
#[inline]
fn to_const_value(self) -> Result<ConstValue, RuntimeError> {
Ok(ConstValue::try_from(self)?)
}
}
impl fmt::Debug for ConstValue {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.kind.fmt(f)
}
}
pub trait FromConstValue: Sized {
fn from_const_value(value: ConstValue) -> Result<Self, RuntimeError>;
}
impl FromConstValue for ConstValue {
#[inline]
fn from_const_value(value: ConstValue) -> Result<Self, RuntimeError> {
Ok(value)
}
}
pub trait ConstConstruct: 'static + Send + Sync {
#[doc(hidden)]
fn const_construct(&self, fields: &[ConstValue]) -> Result<Value, RuntimeError>;
#[doc(hidden)]
fn runtime_construct(&self, fields: &mut [Value]) -> Result<Value, RuntimeError>;
}
pub(crate) trait ConstContext {
fn get(&self, hash: Hash) -> Option<&dyn ConstConstruct>;
}
pub(crate) struct EmptyConstContext;
impl ConstContext for EmptyConstContext {
#[inline]
fn get(&self, _: Hash) -> Option<&dyn ConstConstruct> {
None
}
}