1use std::collections::BTreeMap;
4use std::fmt;
5
6#[derive(Clone, PartialEq)]
8pub enum Value {
9 Nil,
11 Bool(bool),
13 Number(f64),
15 String(String),
17 Array(ArrayValue),
19 Object(ObjectValue),
21 Table(TableValue),
23 Function(FunctionHandle),
25 Native(NativeHandle),
27 Host(HostHandle),
29 Task(TaskHandle),
31}
32
33impl Value {
34 #[must_use]
36 pub const fn nil() -> Self {
37 Self::Nil
38 }
39
40 #[must_use]
42 pub const fn bool(value: bool) -> Self {
43 Self::Bool(value)
44 }
45
46 #[must_use]
48 pub const fn number(value: f64) -> Self {
49 Self::Number(value)
50 }
51
52 #[must_use]
54 pub fn string(value: impl Into<String>) -> Self {
55 Self::String(value.into())
56 }
57
58 #[must_use]
60 pub fn array(elements: impl Into<Vec<Value>>) -> Self {
61 Self::Array(ArrayValue::new(elements))
62 }
63
64 #[must_use]
66 pub fn object(fields: impl IntoIterator<Item = (String, Value)>) -> Self {
67 Self::Object(ObjectValue::new(fields))
68 }
69
70 #[must_use]
72 pub const fn table(id: TableId) -> Self {
73 Self::Table(TableValue::new(id))
74 }
75
76 #[must_use]
78 pub const fn function(id: FunctionId) -> Self {
79 Self::Function(FunctionHandle::new(id))
80 }
81
82 #[must_use]
84 pub const fn native(id: NativeId) -> Self {
85 Self::Native(NativeHandle::new(id))
86 }
87
88 #[must_use]
90 pub const fn host(id: HostId, generation: u64) -> Self {
91 Self::Host(HostHandle::new(id, generation))
92 }
93
94 #[must_use]
96 pub const fn task(id: TaskId) -> Self {
97 Self::Task(TaskHandle::new(id))
98 }
99
100 #[must_use]
102 pub const fn type_name(&self) -> &'static str {
103 match self {
104 Self::Nil => "nil",
105 Self::Bool(_) => "bool",
106 Self::Number(_) => "number",
107 Self::String(_) => "string",
108 Self::Array(_) => "array",
109 Self::Object(_) => "object",
110 Self::Table(_) => "table",
111 Self::Function(_) => "function",
112 Self::Native(_) | Self::Host(_) => "native",
113 Self::Task(_) => "task",
114 }
115 }
116}
117
118impl fmt::Display for Value {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 match self {
121 Self::Nil => f.write_str("nil"),
122 Self::Bool(value) => write!(f, "{value}"),
123 Self::Number(value) => write!(f, "{value}"),
124 Self::String(value) => f.write_str(value),
125 Self::Array(value) => fmt_array(f, value.elements()),
126 Self::Object(value) => fmt_object(f, value.fields()),
127 Self::Table(value) => write!(f, "<table #{}>", value.id().get()),
128 Self::Function(value) => write!(f, "<function #{}>", value.id().get()),
129 Self::Native(value) => write!(f, "<native #{}>", value.id().get()),
130 Self::Host(value) => write!(f, "<host #{}>", value.id().get()),
131 Self::Task(value) => write!(f, "<task #{}>", value.id().get()),
132 }
133 }
134}
135
136impl fmt::Debug for Value {
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 match self {
139 Self::String(value) => f.debug_tuple("String").field(value).finish(),
140 Self::Array(value) => f.debug_tuple("Array").field(value).finish(),
141 Self::Object(value) => f.debug_tuple("Object").field(value).finish(),
142 other => write!(f, "{}({other})", other.type_name()),
143 }
144 }
145}
146
147#[derive(Clone, Debug, PartialEq)]
149pub struct ArrayValue {
150 elements: Vec<Value>,
151}
152
153impl ArrayValue {
154 #[must_use]
156 pub fn new(elements: impl Into<Vec<Value>>) -> Self {
157 Self {
158 elements: elements.into(),
159 }
160 }
161
162 #[must_use]
164 pub fn elements(&self) -> &[Value] {
165 &self.elements
166 }
167}
168
169#[derive(Clone, PartialEq)]
171pub struct ObjectValue {
172 fields: BTreeMap<String, Value>,
173}
174
175impl fmt::Debug for ObjectValue {
176 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177 f.debug_map().entries(self.fields.iter()).finish()
178 }
179}
180
181impl ObjectValue {
182 #[must_use]
184 pub fn new(fields: impl IntoIterator<Item = (String, Value)>) -> Self {
185 Self {
186 fields: fields.into_iter().collect(),
187 }
188 }
189
190 #[must_use]
192 pub const fn fields(&self) -> &BTreeMap<String, Value> {
193 &self.fields
194 }
195}
196
197#[derive(Clone, Copy, Debug, Eq, PartialEq)]
199pub struct TableValue {
200 id: TableId,
201}
202
203impl TableValue {
204 #[must_use]
206 pub const fn new(id: TableId) -> Self {
207 Self { id }
208 }
209
210 #[must_use]
212 pub const fn id(self) -> TableId {
213 self.id
214 }
215}
216
217#[derive(Clone, Copy, Debug, Eq, PartialEq)]
219pub struct FunctionHandle {
220 id: FunctionId,
221}
222
223impl FunctionHandle {
224 #[must_use]
226 pub const fn new(id: FunctionId) -> Self {
227 Self { id }
228 }
229
230 #[must_use]
232 pub const fn id(self) -> FunctionId {
233 self.id
234 }
235}
236
237#[derive(Clone, Copy, Debug, Eq, PartialEq)]
239pub struct NativeHandle {
240 id: NativeId,
241}
242
243impl NativeHandle {
244 #[must_use]
246 pub const fn new(id: NativeId) -> Self {
247 Self { id }
248 }
249
250 #[must_use]
252 pub const fn id(self) -> NativeId {
253 self.id
254 }
255}
256
257#[derive(Clone, Copy, Debug, Eq, PartialEq)]
259pub struct HostHandle {
260 id: HostId,
261 generation: u64,
262}
263
264impl HostHandle {
265 #[must_use]
267 pub const fn new(id: HostId, generation: u64) -> Self {
268 Self { id, generation }
269 }
270
271 #[must_use]
273 pub const fn id(self) -> HostId {
274 self.id
275 }
276
277 #[must_use]
279 pub const fn generation(self) -> u64 {
280 self.generation
281 }
282}
283
284#[derive(Clone, Copy, Debug, Eq, PartialEq)]
286pub struct TaskHandle {
287 id: TaskId,
288}
289
290impl TaskHandle {
291 #[must_use]
293 pub const fn new(id: TaskId) -> Self {
294 Self { id }
295 }
296
297 #[must_use]
299 pub const fn id(self) -> TaskId {
300 self.id
301 }
302}
303
304macro_rules! id_type {
305 ($name:ident, $doc:literal) => {
306 #[doc = $doc]
307 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
308 pub struct $name(u64);
309
310 impl $name {
311 #[must_use]
313 pub const fn new(raw: u64) -> Self {
314 Self(raw)
315 }
316
317 #[must_use]
319 pub const fn get(self) -> u64 {
320 self.0
321 }
322 }
323 };
324}
325
326id_type!(TableId, "Stable identity for a runtime table.");
327id_type!(FunctionId, "Stable identity for a script function.");
328id_type!(NativeId, "Stable identity for a host/native value.");
329id_type!(HostId, "Stable identity for an opaque host object.");
330id_type!(TaskId, "Stable identity for a task.");
331
332fn fmt_array(f: &mut fmt::Formatter<'_>, elements: &[Value]) -> fmt::Result {
333 f.write_str("[")?;
334 for (index, element) in elements.iter().enumerate() {
335 if index > 0 {
336 f.write_str(", ")?;
337 }
338 write!(f, "{element:?}")?;
339 }
340 f.write_str("]")
341}
342
343fn fmt_object(f: &mut fmt::Formatter<'_>, fields: &BTreeMap<String, Value>) -> fmt::Result {
344 f.write_str("{")?;
345 for (index, (key, value)) in fields.iter().enumerate() {
346 if index > 0 {
347 f.write_str(", ")?;
348 }
349 write!(f, "{key}: {value:?}")?;
350 }
351 f.write_str("}")
352}
353
354#[cfg(test)]
355mod tests {
356 use super::{FunctionId, HostId, NativeId, TableId, TaskId, Value};
357
358 #[test]
359 fn constructs_core_values() {
360 assert_eq!(Value::nil().type_name(), "nil");
361 assert_eq!(Value::bool(true).type_name(), "bool");
362 assert_eq!(Value::number(1.5).type_name(), "number");
363 assert_eq!(Value::string("hi").type_name(), "string");
364 assert_eq!(Value::array([Value::Nil]).type_name(), "array");
365 assert_eq!(
366 Value::object([(String::from("hp"), Value::number(10.0))]).type_name(),
367 "object"
368 );
369 assert_eq!(Value::table(TableId::new(1)).type_name(), "table");
370 assert_eq!(Value::function(FunctionId::new(2)).type_name(), "function");
371 assert_eq!(Value::native(NativeId::new(3)).type_name(), "native");
372 assert_eq!(Value::host(HostId::new(4), 1).type_name(), "native");
373 assert_eq!(Value::task(TaskId::new(4)).type_name(), "task");
374 }
375
376 #[test]
377 fn formats_values_for_display_and_debug() {
378 let object = Value::object([
379 (String::from("alive"), Value::bool(true)),
380 (String::from("name"), Value::string("Slime")),
381 ]);
382 let array = Value::array([Value::number(1.0), object]);
383
384 assert_eq!(Value::Nil.to_string(), "nil");
385 assert_eq!(Value::string("hello").to_string(), "hello");
386 assert_eq!(Value::table(TableId::new(7)).to_string(), "<table #7>");
387 assert_eq!(Value::host(HostId::new(8), 1).to_string(), "<host #8>");
388 assert_eq!(
389 array.to_string(),
390 "[number(1), Object({\"alive\": bool(true), \"name\": String(\"Slime\")})]"
391 );
392 }
393
394 #[test]
395 fn compares_primitive_and_plain_values() {
396 assert_eq!(Value::nil(), Value::Nil);
397 assert_eq!(Value::number(2.0), Value::number(2.0));
398 assert_ne!(Value::number(f64::NAN), Value::number(f64::NAN));
399
400 let left = Value::object([(String::from("items"), Value::array([Value::string("key")]))]);
401 let right = Value::object([(String::from("items"), Value::array([Value::string("key")]))]);
402
403 assert_eq!(left, right);
404 }
405
406 #[test]
407 fn keeps_objects_and_tables_distinct() {
408 let object = Value::object([(String::from("id"), Value::number(1.0))]);
409 let table = Value::table(TableId::new(1));
410
411 assert_ne!(object, table);
412 assert_eq!(Value::table(TableId::new(1)), Value::table(TableId::new(1)));
413 assert_ne!(Value::table(TableId::new(1)), Value::table(TableId::new(2)));
414 }
415}