1use std::sync::Arc;
2
3use serde::{Deserialize, Serialize};
4
5use crate::id::DefinitionId;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub enum ValueType {
10 Int,
11 Float,
12 Bool,
13 String,
14 List,
15 DivertTarget,
16 VariablePointer,
17 TempPointer,
18 Null,
19 FragmentRef,
20}
21
22#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
30pub enum Value {
31 Int(i32),
32 Float(f32),
33 Bool(bool),
34 String(Arc<str>),
35 List(Arc<ListValue>),
36 DivertTarget(DefinitionId),
37 VariablePointer(DefinitionId),
39 TempPointer {
42 slot: u16,
43 frame_depth: u16,
44 },
45 Null,
46 FragmentRef(u32),
49}
50
51impl Value {
52 pub fn value_type(&self) -> ValueType {
54 match self {
55 Self::Int(_) => ValueType::Int,
56 Self::Float(_) => ValueType::Float,
57 Self::Bool(_) => ValueType::Bool,
58 Self::String(_) => ValueType::String,
59 Self::List(_) => ValueType::List,
60 Self::DivertTarget(_) => ValueType::DivertTarget,
61 Self::VariablePointer(_) => ValueType::VariablePointer,
62 Self::TempPointer { .. } => ValueType::TempPointer,
63 Self::Null => ValueType::Null,
64 Self::FragmentRef(_) => ValueType::FragmentRef,
65 }
66 }
67
68 pub fn as_int(&self) -> Option<i32> {
74 match self {
75 Self::Int(i) => Some(*i),
76 _ => None,
77 }
78 }
79
80 pub fn as_float(&self) -> Option<f32> {
86 match self {
87 Self::Float(f) => Some(*f),
88 #[expect(
89 clippy::cast_precision_loss,
90 reason = "int->float promotion matches ink coercion semantics"
91 )]
92 Self::Int(i) => Some(*i as f32),
93 _ => None,
94 }
95 }
96
97 pub fn as_bool(&self) -> Option<bool> {
102 match self {
103 Self::Bool(b) => Some(*b),
104 _ => None,
105 }
106 }
107
108 pub fn as_str(&self) -> Option<&str> {
110 match self {
111 Self::String(s) => Some(s),
112 _ => None,
113 }
114 }
115}
116
117impl From<i32> for Value {
118 fn from(v: i32) -> Self {
119 Self::Int(v)
120 }
121}
122
123impl From<f32> for Value {
124 fn from(v: f32) -> Self {
125 Self::Float(v)
126 }
127}
128
129impl From<bool> for Value {
130 fn from(v: bool) -> Self {
131 Self::Bool(v)
132 }
133}
134
135impl From<&str> for Value {
136 fn from(v: &str) -> Self {
137 Self::String(Arc::from(v))
138 }
139}
140
141impl From<String> for Value {
142 fn from(v: String) -> Self {
143 Self::String(Arc::from(v))
144 }
145}
146
147impl From<Arc<str>> for Value {
148 fn from(v: Arc<str>) -> Self {
149 Self::String(v)
150 }
151}
152
153impl From<()> for Value {
154 fn from((): ()) -> Self {
157 Self::Null
158 }
159}
160
161#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
163pub struct ListValue {
164 pub items: Vec<DefinitionId>,
166 pub origins: Vec<DefinitionId>,
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173 use crate::id::DefinitionTag;
174
175 #[test]
176 fn value_type_discriminant() {
177 assert_eq!(Value::Int(0).value_type(), ValueType::Int);
178 assert_eq!(Value::Float(0.0).value_type(), ValueType::Float);
179 assert_eq!(Value::Bool(true).value_type(), ValueType::Bool);
180 assert_eq!(Value::String("".into()).value_type(), ValueType::String);
181 assert_eq!(Value::Null.value_type(), ValueType::Null);
182
183 let list = ListValue {
184 items: vec![],
185 origins: vec![],
186 };
187 assert_eq!(Value::List(list.into()).value_type(), ValueType::List);
188
189 let target = DefinitionId::new(DefinitionTag::Address, 1);
190 assert_eq!(
191 Value::DivertTarget(target).value_type(),
192 ValueType::DivertTarget
193 );
194 }
195
196 #[test]
197 fn from_impls_roundtrip() {
198 assert_eq!(Value::from(7_i32), Value::Int(7));
199 assert_eq!(Value::from(1.5_f32), Value::Float(1.5));
200 assert_eq!(Value::from(true), Value::Bool(true));
201 assert_eq!(Value::from("hi"), Value::String("hi".into()));
202 assert_eq!(Value::from(String::from("hi")), Value::String("hi".into()));
203 assert_eq!(Value::from(()), Value::Null);
204 }
205
206 #[test]
207 fn accessors_are_strict_except_int_to_float() {
208 assert_eq!(Value::Int(3).as_int(), Some(3));
209 assert_eq!(Value::Float(3.0).as_int(), None);
210 assert_eq!(Value::Bool(true).as_int(), None);
211
212 assert_eq!(Value::Int(3).as_float(), Some(3.0));
215 assert_eq!(Value::Float(2.5).as_float(), Some(2.5));
216
217 assert_eq!(Value::Bool(true).as_bool(), Some(true));
218 assert_eq!(Value::Int(1).as_bool(), None);
219
220 assert_eq!(Value::String("x".into()).as_str(), Some("x"));
221 assert_eq!(Value::Int(1).as_str(), None);
222 }
223}