1use std::collections::BTreeMap;
2use std::rc::{Rc, Weak};
3use std::sync::Arc;
4
5use super::resource::validate_runtime_value;
6use super::{diagnostic, DataValue, Env};
7use crate::value::{SemanticValue, ValueView};
8use crate::{CompiledFunction, Constant, Diagnostic};
9
10#[derive(Clone)]
11pub(super) struct Closure {
12 pub(super) function: Arc<CompiledFunction>,
13 pub(super) env: Weak<Env>,
14}
15
16#[derive(Clone)]
17pub(super) struct EnumValue {
18 pub(super) enum_name: Arc<str>,
19 pub(super) variant: Arc<str>,
20 pub(super) fields: Rc<Vec<RuntimeValue>>,
21}
22
23impl EnumValue {
24 pub(super) fn is_variant(&self, enum_name: &str, variant: &str) -> bool {
25 self.enum_name.as_ref() == enum_name && self.variant.as_ref() == variant
26 }
27}
28
29#[derive(Clone)]
30pub(super) enum RuntimeValue {
31 Nil,
32 Bool(bool),
33 Int(i64),
34 Float(f64),
35 String(Arc<str>),
36 Bytes(Arc<[u8]>),
37 List(Rc<Vec<RuntimeValue>>),
38 Record(Rc<BTreeMap<String, RuntimeValue>>),
39 Enum(Rc<EnumValue>),
40 Closure(Closure),
41 Builtin(String),
42 Harness(String),
43}
44
45impl RuntimeValue {
46 pub(super) fn truthy(&self) -> bool {
47 match self {
48 Self::Nil | Self::Bool(false) => false,
49 Self::Int(0) => false,
50 Self::Float(value) if *value == 0.0 || value.is_nan() => false,
51 Self::String(value) => !value.is_empty(),
52 Self::List(value) => !value.is_empty(),
53 Self::Record(value) => !value.is_empty(),
54 _ => true,
55 }
56 }
57
58 pub(super) fn display(&self) -> String {
59 match self {
60 Self::Nil => "nil".into(),
61 Self::Bool(value) => value.to_string(),
62 Self::Int(value) => value.to_string(),
63 Self::Float(value) => value.to_string(),
64 Self::String(value) => value.to_string(),
65 Self::Bytes(value) => format!("<{} bytes>", value.len()),
66 Self::List(values) => format!(
67 "[{}]",
68 values
69 .iter()
70 .map(Self::display)
71 .collect::<Vec<_>>()
72 .join(", ")
73 ),
74 Self::Record(values) => format!(
75 "{{{}}}",
76 values
77 .iter()
78 .map(|(key, value)| format!("{key}: {}", value.display()))
79 .collect::<Vec<_>>()
80 .join(", ")
81 ),
82 Self::Enum(value) if value.fields.is_empty() => {
83 format!("{}.{}", value.enum_name, value.variant)
84 }
85 Self::Enum(value) => format!(
86 "{}.{}({})",
87 value.enum_name,
88 value.variant,
89 value
90 .fields
91 .iter()
92 .map(Self::display)
93 .collect::<Vec<_>>()
94 .join(", ")
95 ),
96 Self::Closure(_) => "<closure>".into(),
97 Self::Builtin(name) => format!("<builtin {name}>"),
98 Self::Harness(name) => format!("<harness {name}>"),
99 }
100 }
101}
102
103impl SemanticValue for RuntimeValue {
104 fn semantic_view(&self) -> ValueView<'_, Self> {
105 match self {
106 Self::Nil => ValueView::Nil,
107 Self::Bool(value) => ValueView::Bool(*value),
108 Self::Int(value) => ValueView::Int(*value),
109 Self::Float(value) => ValueView::Float(*value),
110 Self::String(value) => ValueView::String(value),
111 Self::Bytes(value) => ValueView::Bytes(value),
112 Self::List(values) => ValueView::List(values),
113 Self::Record(values) => ValueView::Record(values),
114 Self::Enum(value) => ValueView::Enum {
115 enum_name: &value.enum_name,
116 variant: &value.variant,
117 fields: &value.fields,
118 },
119 Self::Closure(_) | Self::Builtin(_) | Self::Harness(_) => ValueView::Opaque,
120 }
121 }
122}
123
124impl From<DataValue> for RuntimeValue {
125 fn from(value: DataValue) -> Self {
126 match value {
127 DataValue::Nil => Self::Nil,
128 DataValue::Bool(value) => Self::Bool(value),
129 DataValue::Int(value) => Self::Int(value),
130 DataValue::Float(value) => Self::Float(value),
131 DataValue::String(value) => Self::String(Arc::from(value)),
132 DataValue::Bytes(value) => Self::Bytes(Arc::from(value)),
133 DataValue::List(values) => {
134 Self::List(Rc::new(values.into_iter().map(Self::from).collect()))
135 }
136 DataValue::Record(values) => Self::Record(Rc::new(
137 values
138 .into_iter()
139 .map(|(key, value)| (key, Self::from(value)))
140 .collect(),
141 )),
142 }
143 }
144}
145
146impl From<Constant> for RuntimeValue {
147 fn from(value: Constant) -> Self {
148 match value {
149 Constant::Int(value) => Self::Int(value),
150 Constant::Float(value) => Self::Float(value),
151 Constant::String(value) => Self::String(Arc::from(value)),
152 Constant::Bool(value) => Self::Bool(value),
153 Constant::Nil => Self::Nil,
154 Constant::Duration(value) => Self::Int(value),
155 }
156 }
157}
158
159impl TryFrom<RuntimeValue> for DataValue {
160 type Error = Diagnostic;
161
162 fn try_from(value: RuntimeValue) -> Result<Self, Self::Error> {
163 validate_runtime_value(&value)?;
164 Self::try_from_validated(value)
165 }
166}
167
168impl DataValue {
169 fn try_from_validated(value: RuntimeValue) -> Result<Self, Diagnostic> {
170 Ok(match value {
171 RuntimeValue::Nil => Self::Nil,
172 RuntimeValue::Bool(value) => Self::Bool(value),
173 RuntimeValue::Int(value) => Self::Int(value),
174 RuntimeValue::Float(value) => Self::Float(value),
175 RuntimeValue::String(value) => Self::String(value.to_string()),
176 RuntimeValue::Bytes(value) => Self::Bytes(value.to_vec()),
177 RuntimeValue::List(values) => Self::List(
178 Rc::unwrap_or_clone(values)
179 .into_iter()
180 .map(Self::try_from_validated)
181 .collect::<Result<_, _>>()?,
182 ),
183 RuntimeValue::Record(values) => Self::Record(
184 Rc::unwrap_or_clone(values)
185 .into_iter()
186 .map(|(key, value)| Ok((key, Self::try_from_validated(value)?)))
187 .collect::<Result<_, Diagnostic>>()?,
188 ),
189 RuntimeValue::Enum(_) => {
190 return Err(diagnostic(
191 "non_data_result",
192 "execution returned an enum outside the portable data contract",
193 ));
194 }
195 RuntimeValue::Closure(_) | RuntimeValue::Builtin(_) | RuntimeValue::Harness(_) => {
196 return Err(diagnostic(
197 "non_data_result",
198 "execution returned a host or callable value",
199 ));
200 }
201 })
202 }
203}