1use super::conversions::{FromLustValue, FunctionArgs, IntoTypedValue};
2use super::program::{ensure_return_type, normalize_global_name, EmbeddedProgram};
3use crate::ast::{Type, TypeKind};
4use crate::bytecode::{FieldStorage, LustMap, StructLayout, Value, ValueKey};
5use crate::number::{LustFloat, LustInt};
6use crate::typechecker::FunctionSignature;
7use crate::{LustError, Result};
8use std::cell::{Ref, RefCell, RefMut};
9use std::ops::Deref;
10use std::rc::Rc;
11
12pub struct TypedValue {
13 value: Value,
14 matcher: Box<dyn Fn(&Value, &Type) -> bool>,
15 description: &'static str,
16}
17
18impl TypedValue {
19 pub(crate) fn new<F>(value: Value, matcher: F, description: &'static str) -> Self
20 where
21 F: Fn(&Value, &Type) -> bool + 'static,
22 {
23 Self {
24 value,
25 matcher: Box::new(matcher),
26 description,
27 }
28 }
29
30 pub(crate) fn matches(&self, ty: &Type) -> bool {
31 match &ty.kind {
32 TypeKind::Union(types) => types.iter().any(|alt| (self.matcher)(&self.value, alt)),
33 _ => (self.matcher)(&self.value, ty),
34 }
35 }
36
37 pub(crate) fn description(&self) -> &'static str {
38 self.description
39 }
40
41 pub(crate) fn into_value(self) -> Value {
42 self.value
43 }
44
45 pub(crate) fn as_value(&self) -> &Value {
46 &self.value
47 }
48}
49
50pub struct StructField {
51 name: String,
52 value: TypedValue,
53}
54
55impl StructField {
56 pub fn new(name: impl Into<String>, value: impl IntoTypedValue) -> Self {
57 Self {
58 name: name.into(),
59 value: value.into_typed_value(),
60 }
61 }
62
63 pub fn name(&self) -> &str {
64 &self.name
65 }
66
67 pub(crate) fn into_parts(self) -> (String, TypedValue) {
68 (self.name, self.value)
69 }
70}
71
72pub fn struct_field(name: impl Into<String>, value: impl IntoTypedValue) -> StructField {
73 StructField::new(name, value)
74}
75
76impl<K, V> From<(K, V)> for StructField
77where
78 K: Into<String>,
79 V: IntoTypedValue,
80{
81 fn from((name, value): (K, V)) -> Self {
82 StructField::new(name, value)
83 }
84}
85
86#[derive(Clone)]
87pub struct StructInstance {
88 type_name: String,
89 value: Value,
90}
91
92impl StructInstance {
93 pub(crate) fn new(type_name: String, value: Value) -> Self {
94 debug_assert!(matches!(value, Value::Struct { .. }));
95 Self { type_name, value }
96 }
97
98 pub fn type_name(&self) -> &str {
99 &self.type_name
100 }
101
102 pub fn field<T: FromLustValue>(&self, field: &str) -> Result<T> {
103 let value_ref = self.borrow_field(field)?;
104 T::from_value(value_ref.into_owned())
105 }
106
107 pub fn borrow_field(&self, field: &str) -> Result<ValueRef<'_>> {
108 match &self.value {
109 Value::Struct { layout, fields, .. } => {
110 let index = layout
111 .index_of_str(field)
112 .ok_or_else(|| LustError::RuntimeError {
113 message: format!(
114 "Struct '{}' has no field named '{}'",
115 self.type_name, field
116 ),
117 })?;
118 match layout.field_storage(index) {
119 FieldStorage::Strong => {
120 let slots = fields.borrow();
121 if slots.get(index).is_none() {
122 return Err(LustError::RuntimeError {
123 message: format!(
124 "Struct '{}' field '{}' is unavailable",
125 self.type_name, field
126 ),
127 });
128 }
129
130 Ok(ValueRef::borrowed(Ref::map(slots, move |values| {
131 &values[index]
132 })))
133 }
134
135 FieldStorage::Weak => {
136 let stored = {
137 let slots = fields.borrow();
138 slots
139 .get(index)
140 .cloned()
141 .ok_or_else(|| LustError::RuntimeError {
142 message: format!(
143 "Struct '{}' field '{}' is unavailable",
144 self.type_name, field
145 ),
146 })?
147 };
148 let materialized = layout.materialize_field_value(index, stored);
149 Ok(ValueRef::owned(materialized))
150 }
151 }
152 }
153
154 _ => Err(LustError::RuntimeError {
155 message: "StructInstance does not contain a struct value".to_string(),
156 }),
157 }
158 }
159
160 pub fn set_field<V: IntoTypedValue>(&self, field: &str, value: V) -> Result<()> {
161 match &self.value {
162 Value::Struct { layout, fields, .. } => {
163 let index = layout
164 .index_of_str(field)
165 .ok_or_else(|| LustError::RuntimeError {
166 message: format!(
167 "Struct '{}' has no field named '{}'",
168 self.type_name, field
169 ),
170 })?;
171 let typed_value = value.into_typed_value();
172 let matches_declared = typed_value.matches(layout.field_type(index));
173 let matches_ref_inner = layout.is_weak(index)
174 && layout
175 .weak_target(index)
176 .map(|inner| typed_value.matches(inner))
177 .unwrap_or(false);
178 if !(matches_declared || matches_ref_inner) {
179 return Err(LustError::TypeError {
180 message: format!(
181 "Struct '{}' field '{}' expects Lust type '{}' but Rust provided '{}'",
182 self.type_name,
183 field,
184 layout.field_type(index),
185 typed_value.description()
186 ),
187 });
188 }
189
190 let canonical_value = layout
191 .canonicalize_field_value(index, typed_value.into_value())
192 .map_err(|message| LustError::TypeError { message })?;
193 fields.borrow_mut()[index] = canonical_value;
194 Ok(())
195 }
196
197 _ => Err(LustError::RuntimeError {
198 message: "StructInstance does not contain a struct value".to_string(),
199 }),
200 }
201 }
202
203 pub fn update_field<F, V>(&self, field: &str, update: F) -> Result<()>
204 where
205 F: FnOnce(Value) -> Result<V>,
206 V: IntoTypedValue,
207 {
208 match &self.value {
209 Value::Struct { layout, fields, .. } => {
210 let index = layout
211 .index_of_str(field)
212 .ok_or_else(|| LustError::RuntimeError {
213 message: format!(
214 "Struct '{}' has no field named '{}'",
215 self.type_name, field
216 ),
217 })?;
218 let mut slots = fields.borrow_mut();
219 let slot = slots
220 .get_mut(index)
221 .ok_or_else(|| LustError::RuntimeError {
222 message: format!(
223 "Struct '{}' field '{}' is unavailable",
224 self.type_name, field
225 ),
226 })?;
227 let fallback = slot.clone();
228 let current_canonical = std::mem::replace(slot, Value::Nil);
229 let current_materialized = layout.materialize_field_value(index, current_canonical);
230 let updated = match update(current_materialized) {
231 Ok(value) => value,
232 Err(err) => {
233 *slot = fallback;
234 return Err(err);
235 }
236 };
237 let typed_value = updated.into_typed_value();
238 let matches_declared = typed_value.matches(layout.field_type(index));
239 let matches_ref_inner = layout.is_weak(index)
240 && layout
241 .weak_target(index)
242 .map(|inner| typed_value.matches(inner))
243 .unwrap_or(false);
244 if !(matches_declared || matches_ref_inner) {
245 *slot = fallback;
246 return Err(LustError::TypeError {
247 message: format!(
248 "Struct '{}' field '{}' expects Lust type '{}' but Rust provided '{}'",
249 self.type_name,
250 field,
251 layout.field_type(index),
252 typed_value.description()
253 ),
254 });
255 }
256
257 let canonical_value = layout
258 .canonicalize_field_value(index, typed_value.into_value())
259 .map_err(|message| LustError::TypeError { message })?;
260 *slot = canonical_value;
261 Ok(())
262 }
263
264 _ => Err(LustError::RuntimeError {
265 message: "StructInstance does not contain a struct value".to_string(),
266 }),
267 }
268 }
269
270 pub fn as_value(&self) -> &Value {
271 &self.value
272 }
273
274 pub(crate) fn into_value(self) -> Value {
275 self.value
276 }
277}
278
279#[derive(Clone)]
280pub struct FunctionHandle {
281 value: Value,
282}
283
284impl FunctionHandle {
285 pub(crate) fn is_callable_value(value: &Value) -> bool {
286 matches!(
287 value,
288 Value::Function(_) | Value::Closure { .. } | Value::NativeFunction(_)
289 )
290 }
291
292 pub(crate) fn new_unchecked(value: Value) -> Self {
293 Self { value }
294 }
295
296 pub fn from_value(value: Value) -> Result<Self> {
297 if Self::is_callable_value(&value) {
298 Ok(Self::new_unchecked(value))
299 } else {
300 Err(LustError::RuntimeError {
301 message: format!("Expected Lust value 'function' but received '{:?}'", value),
302 })
303 }
304 }
305
306 pub fn as_value(&self) -> &Value {
307 &self.value
308 }
309
310 pub fn into_value(self) -> Value {
311 self.value
312 }
313
314 fn function_index(&self) -> Option<usize> {
315 match &self.value {
316 Value::Function(idx) => Some(*idx),
317 Value::Closure { function_idx, .. } => Some(*function_idx),
318 _ => None,
319 }
320 }
321
322 fn function_name<'a>(&'a self, program: &'a EmbeddedProgram) -> Option<&'a str> {
323 let idx = self.function_index()?;
324 program.vm().function_name(idx)
325 }
326
327 pub fn signature<'a>(
328 &'a self,
329 program: &'a EmbeddedProgram,
330 ) -> Option<(&'a str, &'a FunctionSignature)> {
331 let name = self.function_name(program)?;
332 program.signature(name).map(|sig| (name, sig))
333 }
334
335 pub fn matches_signature(
336 &self,
337 program: &EmbeddedProgram,
338 expected: &FunctionSignature,
339 ) -> bool {
340 match self.signature(program) {
341 Some((_, actual)) => signatures_match(actual, expected),
342 None => false,
343 }
344 }
345
346 pub fn validate_signature(
347 &self,
348 program: &EmbeddedProgram,
349 expected: &FunctionSignature,
350 ) -> Result<()> {
351 let (name, actual) = self.signature(program).ok_or_else(|| LustError::TypeError {
352 message: "No type information available for function value; use call_raw if the function is dynamically typed"
353 .into(),
354 })?;
355 if signatures_match(actual, expected) {
356 Ok(())
357 } else {
358 Err(LustError::TypeError {
359 message: format!(
360 "Function '{}' signature mismatch: expected {}, found {}",
361 name,
362 signature_to_string(expected),
363 signature_to_string(actual)
364 ),
365 })
366 }
367 }
368
369 pub fn call_raw(&self, program: &mut EmbeddedProgram, args: Vec<Value>) -> Result<Value> {
370 program.vm_mut().call_value(self.as_value(), args)
371 }
372
373 pub fn call_typed<Args, R>(&self, program: &mut EmbeddedProgram, args: Args) -> Result<R>
374 where
375 Args: FunctionArgs,
376 R: FromLustValue,
377 {
378 let program_ref: &EmbeddedProgram = &*program;
379 let values = args.into_values();
380 if let Some((name, signature)) = self.signature(program_ref) {
381 Args::validate_signature(name, &signature.params)?;
382 ensure_return_type::<R>(name, &signature.return_type)?;
383 let value = program.vm_mut().call_value(self.as_value(), values)?;
384 R::from_value(value)
385 } else {
386 let value = program.vm_mut().call_value(self.as_value(), values)?;
387 R::from_value(value)
388 }
389 }
390}
391
392#[derive(Clone)]
393pub struct StructHandle {
394 instance: StructInstance,
395}
396
397impl StructHandle {
398 fn from_instance(instance: StructInstance) -> Self {
399 Self { instance }
400 }
401
402 fn from_parts(
403 name: &String,
404 layout: &Rc<StructLayout>,
405 fields: &Rc<RefCell<Vec<Value>>>,
406 ) -> Self {
407 let value = Value::Struct {
408 name: name.clone(),
409 layout: layout.clone(),
410 fields: fields.clone(),
411 };
412 Self::from_instance(StructInstance::new(name.clone(), value))
413 }
414
415 pub fn from_value(value: Value) -> Result<Self> {
416 <StructInstance as FromLustValue>::from_value(value).map(StructHandle::from)
417 }
418
419 pub fn type_name(&self) -> &str {
420 self.instance.type_name()
421 }
422
423 pub fn field<T: FromLustValue>(&self, field: &str) -> Result<T> {
424 self.instance.field(field)
425 }
426
427 pub fn borrow_field(&self, field: &str) -> Result<ValueRef<'_>> {
428 self.instance.borrow_field(field)
429 }
430
431 pub fn set_field<V: IntoTypedValue>(&self, field: &str, value: V) -> Result<()> {
432 self.instance.set_field(field, value)
433 }
434
435 pub fn update_field<F, V>(&self, field: &str, update: F) -> Result<()>
436 where
437 F: FnOnce(Value) -> Result<V>,
438 V: IntoTypedValue,
439 {
440 self.instance.update_field(field, update)
441 }
442
443 pub fn as_value(&self) -> &Value {
444 self.instance.as_value()
445 }
446
447 pub fn to_instance(&self) -> StructInstance {
448 self.instance.clone()
449 }
450
451 pub fn into_instance(self) -> StructInstance {
452 self.instance
453 }
454
455 pub fn matches_type(&self, expected: &str) -> bool {
456 lust_type_names_match(self.type_name(), expected)
457 }
458
459 pub fn ensure_type(&self, expected: &str) -> Result<()> {
460 if self.matches_type(expected) {
461 Ok(())
462 } else {
463 Err(LustError::TypeError {
464 message: format!(
465 "Struct '{}' does not match expected type '{}'",
466 self.type_name(),
467 expected
468 ),
469 })
470 }
471 }
472}
473
474impl StructInstance {
475 pub fn to_handle(&self) -> StructHandle {
476 StructHandle::from_instance(self.clone())
477 }
478
479 pub fn into_handle(self) -> StructHandle {
480 StructHandle::from_instance(self)
481 }
482}
483
484impl From<StructInstance> for StructHandle {
485 fn from(instance: StructInstance) -> Self {
486 StructHandle::from_instance(instance)
487 }
488}
489
490impl From<StructHandle> for StructInstance {
491 fn from(handle: StructHandle) -> Self {
492 handle.into_instance()
493 }
494}
495
496pub enum ValueRef<'a> {
497 Borrowed(Ref<'a, Value>),
498 Owned(Value),
499}
500
501impl<'a> ValueRef<'a> {
502 fn borrowed(inner: Ref<'a, Value>) -> Self {
503 Self::Borrowed(inner)
504 }
505
506 fn owned(value: Value) -> Self {
507 Self::Owned(value)
508 }
509
510 pub fn as_value(&self) -> &Value {
511 match self {
512 ValueRef::Borrowed(inner) => &*inner,
513 ValueRef::Owned(value) => value,
514 }
515 }
516
517 pub fn to_owned(&self) -> Value {
518 self.as_value().clone()
519 }
520
521 pub fn into_owned(self) -> Value {
522 match self {
523 ValueRef::Borrowed(inner) => inner.clone(),
524 ValueRef::Owned(value) => value,
525 }
526 }
527
528 pub fn as_bool(&self) -> Option<bool> {
529 match self.as_value() {
530 Value::Bool(value) => Some(*value),
531 _ => None,
532 }
533 }
534
535 pub fn as_int(&self) -> Option<LustInt> {
536 self.as_value().as_int()
537 }
538
539 pub fn as_float(&self) -> Option<LustFloat> {
540 self.as_value().as_float()
541 }
542
543 pub fn as_string(&self) -> Option<&str> {
544 self.as_value().as_string()
545 }
546
547 pub fn as_rc_string(&self) -> Option<Rc<String>> {
548 match self.as_value() {
549 Value::String(value) => Some(value.clone()),
550 _ => None,
551 }
552 }
553
554 pub fn as_array_handle(&self) -> Option<ArrayHandle> {
555 match self.as_value() {
556 Value::Array(items) => Some(ArrayHandle::from_rc(items.clone())),
557 _ => None,
558 }
559 }
560
561 pub fn as_map_handle(&self) -> Option<MapHandle> {
562 match self.as_value() {
563 Value::Map(map) => Some(MapHandle::from_rc(map.clone())),
564 _ => None,
565 }
566 }
567
568 pub fn as_struct_handle(&self) -> Option<StructHandle> {
569 match self.as_value() {
570 Value::Struct {
571 name,
572 layout,
573 fields,
574 } => Some(StructHandle::from_parts(name, layout, fields)),
575 Value::WeakStruct(weak) => weak
576 .upgrade()
577 .and_then(|value| StructHandle::from_value(value).ok()),
578 _ => None,
579 }
580 }
581}
582
583pub struct StringRef<'a> {
584 value: ValueRef<'a>,
585}
586
587impl<'a> StringRef<'a> {
588 pub(crate) fn new(value: ValueRef<'a>) -> Self {
589 Self { value }
590 }
591
592 pub fn as_str(&self) -> &str {
593 self.value
594 .as_string()
595 .expect("StringRef must wrap a Lust string")
596 }
597
598 pub fn as_rc(&self) -> Rc<String> {
599 self.value
600 .as_rc_string()
601 .expect("StringRef must wrap a Lust string")
602 }
603
604 pub fn to_value(&self) -> &Value {
605 self.value.as_value()
606 }
607
608 pub fn into_value_ref(self) -> ValueRef<'a> {
609 self.value
610 }
611}
612
613impl<'a> Deref for StringRef<'a> {
614 type Target = str;
615
616 fn deref(&self) -> &Self::Target {
617 self.as_str()
618 }
619}
620
621#[derive(Clone)]
622pub struct ArrayHandle {
623 inner: Rc<RefCell<Vec<Value>>>,
624}
625
626impl ArrayHandle {
627 pub(crate) fn from_rc(inner: Rc<RefCell<Vec<Value>>>) -> Self {
628 Self { inner }
629 }
630
631 pub fn len(&self) -> usize {
632 self.inner.borrow().len()
633 }
634
635 pub fn is_empty(&self) -> bool {
636 self.len() == 0
637 }
638
639 pub fn borrow(&self) -> Ref<'_, [Value]> {
640 Ref::map(self.inner.borrow(), |values| values.as_slice())
641 }
642
643 pub fn borrow_mut(&self) -> RefMut<'_, Vec<Value>> {
644 self.inner.borrow_mut()
645 }
646
647 pub fn push(&self, value: Value) {
648 self.inner.borrow_mut().push(value);
649 }
650
651 pub fn extend<I>(&self, iter: I)
652 where
653 I: IntoIterator<Item = Value>,
654 {
655 self.inner.borrow_mut().extend(iter);
656 }
657
658 pub fn get(&self, index: usize) -> Option<ValueRef<'_>> {
659 {
660 let values = self.inner.borrow();
661 if values.get(index).is_none() {
662 return None;
663 }
664 }
665
666 let values = self.inner.borrow();
667 Some(ValueRef::borrowed(Ref::map(values, move |items| {
668 &items[index]
669 })))
670 }
671
672 pub fn with_ref<R>(&self, f: impl FnOnce(&[Value]) -> R) -> R {
673 let values = self.inner.borrow();
674 f(values.as_slice())
675 }
676
677 pub fn with_mut<R>(&self, f: impl FnOnce(&mut Vec<Value>) -> R) -> R {
678 let mut values = self.inner.borrow_mut();
679 f(&mut values)
680 }
681
682 pub(crate) fn into_value(self) -> Value {
683 Value::Array(self.inner)
684 }
685}
686
687#[derive(Clone)]
688pub struct MapHandle {
689 inner: Rc<RefCell<LustMap>>,
690}
691
692impl MapHandle {
693 pub(crate) fn from_rc(inner: Rc<RefCell<LustMap>>) -> Self {
694 Self { inner }
695 }
696
697 pub fn len(&self) -> usize {
698 self.inner.borrow().len()
699 }
700
701 pub fn is_empty(&self) -> bool {
702 self.len() == 0
703 }
704
705 pub fn borrow(&self) -> Ref<'_, LustMap> {
706 self.inner.borrow()
707 }
708
709 pub fn borrow_mut(&self) -> RefMut<'_, LustMap> {
710 self.inner.borrow_mut()
711 }
712
713 pub fn contains_key<K>(&self, key: K) -> bool
714 where
715 K: Into<ValueKey>,
716 {
717 self.inner.borrow().contains_key(&key.into())
718 }
719
720 pub fn get<K>(&self, key: K) -> Option<ValueRef<'_>>
721 where
722 K: Into<ValueKey>,
723 {
724 let key = key.into();
725 {
726 if !self.inner.borrow().contains_key(&key) {
727 return None;
728 }
729 }
730 let lookup = key.clone();
731 let map = self.inner.borrow();
732 Some(ValueRef::borrowed(Ref::map(
733 map,
734 move |values: &LustMap| {
735 values
736 .get(&lookup)
737 .expect("lookup key should be present after contains_key")
738 },
739 )))
740 }
741
742 pub fn insert<K>(&self, key: K, value: Value) -> Option<Value>
743 where
744 K: Into<ValueKey>,
745 {
746 self.inner.borrow_mut().insert(key.into(), value)
747 }
748
749 pub fn remove<K>(&self, key: K) -> Option<Value>
750 where
751 K: Into<ValueKey>,
752 {
753 self.inner.borrow_mut().remove(&key.into())
754 }
755
756 pub fn with_ref<R>(&self, f: impl FnOnce(&LustMap) -> R) -> R {
757 let map = self.inner.borrow();
758 f(&map)
759 }
760
761 pub fn with_mut<R>(&self, f: impl FnOnce(&mut LustMap) -> R) -> R {
762 let mut map = self.inner.borrow_mut();
763 f(&mut map)
764 }
765
766 pub(crate) fn into_value(self) -> Value {
767 Value::Map(self.inner)
768 }
769}
770
771#[derive(Clone)]
772pub struct EnumInstance {
773 type_name: String,
774 variant: String,
775 value: Value,
776}
777
778impl EnumInstance {
779 pub(crate) fn new(type_name: String, variant: String, value: Value) -> Self {
780 debug_assert!(matches!(value, Value::Enum { .. }));
781 Self {
782 type_name,
783 variant,
784 value,
785 }
786 }
787
788 pub fn type_name(&self) -> &str {
789 &self.type_name
790 }
791
792 pub fn variant(&self) -> &str {
793 &self.variant
794 }
795
796 pub fn payload_len(&self) -> usize {
797 match &self.value {
798 Value::Enum { values, .. } => values.as_ref().map(|v| v.len()).unwrap_or(0),
799 _ => 0,
800 }
801 }
802
803 pub fn payload<T: FromLustValue>(&self, index: usize) -> Result<T> {
804 match &self.value {
805 Value::Enum { values, .. } => {
806 let values = values.as_ref().ok_or_else(|| LustError::RuntimeError {
807 message: format!(
808 "Enum variant '{}.{}' carries no payload",
809 self.type_name, self.variant
810 ),
811 })?;
812 let stored = values
813 .get(index)
814 .cloned()
815 .ok_or_else(|| LustError::RuntimeError {
816 message: format!(
817 "Enum variant '{}.{}' payload index {} is out of bounds",
818 self.type_name, self.variant, index
819 ),
820 })?;
821 T::from_value(stored)
822 }
823
824 _ => Err(LustError::RuntimeError {
825 message: "EnumInstance does not contain an enum value".to_string(),
826 }),
827 }
828 }
829
830 pub fn as_value(&self) -> &Value {
831 &self.value
832 }
833
834 pub(crate) fn into_value(self) -> Value {
835 self.value
836 }
837}
838
839pub(crate) fn matches_lust_struct(value: &Value, ty: &Type) -> bool {
840 match (value, &ty.kind) {
841 (Value::Struct { name, .. }, TypeKind::Named(expected)) => {
842 lust_type_names_match(name, expected)
843 }
844 (Value::Struct { name, .. }, TypeKind::GenericInstance { name: expected, .. }) => {
845 lust_type_names_match(name, expected)
846 }
847
848 (value, TypeKind::Union(types)) => types.iter().any(|alt| matches_lust_struct(value, alt)),
849 (_, TypeKind::Unknown) => true,
850 _ => false,
851 }
852}
853
854pub(crate) fn matches_lust_enum(value: &Value, ty: &Type) -> bool {
855 match (value, &ty.kind) {
856 (Value::Enum { enum_name, .. }, TypeKind::Named(expected)) => {
857 lust_type_names_match(enum_name, expected)
858 }
859 (Value::Enum { enum_name, .. }, TypeKind::GenericInstance { name: expected, .. }) => {
860 lust_type_names_match(enum_name, expected)
861 }
862
863 (value, TypeKind::Union(types)) => types.iter().any(|alt| matches_lust_enum(value, alt)),
864 (_, TypeKind::Unknown) => true,
865 _ => false,
866 }
867}
868
869pub(crate) fn lust_type_names_match(value: &str, expected: &str) -> bool {
870 if value == expected {
871 return true;
872 }
873
874 let normalized_value = normalize_global_name(value);
875 let normalized_expected = normalize_global_name(expected);
876 if normalized_value == normalized_expected {
877 return true;
878 }
879
880 simple_type_name(&normalized_value) == simple_type_name(&normalized_expected)
881}
882
883pub(crate) fn simple_type_name(name: &str) -> &str {
884 name.rsplit(|c| c == '.' || c == ':').next().unwrap_or(name)
885}
886
887pub(crate) fn matches_array_type<F>(ty: &Type, matcher: &F) -> bool
888where
889 F: Fn(&Type) -> bool,
890{
891 match &ty.kind {
892 TypeKind::Array(inner) => matcher(inner),
893 TypeKind::Unknown => true,
894 TypeKind::Union(types) => types.iter().any(|alt| matches_array_type(alt, matcher)),
895 _ => false,
896 }
897}
898
899pub(crate) fn matches_array_handle_type(ty: &Type) -> bool {
900 match &ty.kind {
901 TypeKind::Array(_) | TypeKind::Unknown => true,
902 TypeKind::Union(types) => types.iter().any(|alt| matches_array_handle_type(alt)),
903 _ => false,
904 }
905}
906
907pub(crate) fn matches_map_handle_type(ty: &Type) -> bool {
908 match &ty.kind {
909 TypeKind::Map(_, _) | TypeKind::Unknown => true,
910 TypeKind::Union(types) => types.iter().any(|alt| matches_map_handle_type(alt)),
911 _ => false,
912 }
913}
914
915pub(crate) fn matches_function_handle_type(ty: &Type) -> bool {
916 match &ty.kind {
917 TypeKind::Function { .. } | TypeKind::Unknown => true,
918 TypeKind::Union(types) => types.iter().any(|alt| matches_function_handle_type(alt)),
919 _ => false,
920 }
921}
922
923pub(crate) fn signatures_match(a: &FunctionSignature, b: &FunctionSignature) -> bool {
924 if a.is_method != b.is_method || a.params.len() != b.params.len() {
925 return false;
926 }
927
928 if a.return_type != b.return_type {
929 return false;
930 }
931
932 a.params
933 .iter()
934 .zip(&b.params)
935 .all(|(left, right)| left == right)
936}
937
938pub(crate) fn signature_to_string(signature: &FunctionSignature) -> String {
939 signature.to_string()
940}
941
942#[cfg(test)]
943mod tests {
944 use super::*;
945 use crate::ast::Span;
946 use crate::embed::{
947 struct_field_decl, AsyncDriver, EmbeddedProgram, FunctionBuilder, LustStructView,
948 StructBuilder,
949 };
950 use std::rc::Rc;
951
952 fn serial_guard() -> std::sync::MutexGuard<'static, ()> {
953 static LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
954 LOCK.lock().unwrap_or_else(|err| err.into_inner())
955 }
956
957 fn build_program(source: &str) -> EmbeddedProgram {
958 EmbeddedProgram::builder()
959 .module("main", source)
960 .entry_module("main")
961 .compile()
962 .expect("compile embedded program")
963 }
964
965 #[test]
966 fn module_locals_are_visible_in_functions() {
967 let _guard = serial_guard();
968 let source = r#"
969 local factors: Array<int> = []
970
971 pub function touch()
972 factors:push(1)
973 end
974
975 pub function main()
976 touch()
977 end
978
979 main()
980 "#;
981
982 let mut program = build_program(source);
983 program.run_entry_script().expect("run entry script");
984 }
985
986 #[test]
987 fn struct_instance_supports_mixed_field_types() {
988 let _guard = serial_guard();
989 let source = r#"
990 struct Mixed
991 count: int
992 label: string
993 enabled: bool
994 end
995 "#;
996
997 let program = build_program(source);
998 let mixed = program
999 .struct_instance(
1000 "main.Mixed",
1001 [
1002 struct_field("count", 7_i64),
1003 struct_field("label", "hi"),
1004 struct_field("enabled", true),
1005 ],
1006 )
1007 .expect("struct instance");
1008
1009 assert_eq!(mixed.field::<i64>("count").expect("count field"), 7);
1010 assert_eq!(mixed.field::<String>("label").expect("label field"), "hi");
1011 assert!(mixed.field::<bool>("enabled").expect("enabled field"));
1012 }
1013
1014 #[test]
1015 fn struct_instance_borrow_field_provides_reference_view() {
1016 let _guard = serial_guard();
1017 let source = r#"
1018 struct Sample
1019 name: string
1020 end
1021 "#;
1022
1023 let program = build_program(source);
1024 let sample = program
1025 .struct_instance("main.Sample", [struct_field("name", "Borrowed")])
1026 .expect("struct instance");
1027
1028 let name_ref = sample.borrow_field("name").expect("borrow name field");
1029 assert_eq!(name_ref.as_string().unwrap(), "Borrowed");
1030 assert!(name_ref.as_array_handle().is_none());
1031 }
1032
1033 #[test]
1034 fn array_handle_allows_in_place_mutation() {
1035 let _guard = serial_guard();
1036 let value = Value::array(vec![Value::Int(1)]);
1037 let handle = <ArrayHandle as FromLustValue>::from_value(value).expect("array handle");
1038
1039 {
1040 let mut slots = handle.borrow_mut();
1041 slots.push(Value::Int(2));
1042 slots.push(Value::Int(3));
1043 }
1044
1045 let snapshot: Vec<_> = handle
1046 .borrow()
1047 .iter()
1048 .map(|value| value.as_int().expect("int value"))
1049 .collect();
1050 assert_eq!(snapshot, vec![1, 2, 3]);
1051 }
1052
1053 #[test]
1054 fn struct_instance_allows_setting_fields() {
1055 let _guard = serial_guard();
1056 let source = r#"
1057 struct Mixed
1058 count: int
1059 label: string
1060 enabled: bool
1061 end
1062 "#;
1063
1064 let program = build_program(source);
1065 let mixed = program
1066 .struct_instance(
1067 "main.Mixed",
1068 [
1069 struct_field("count", 1_i64),
1070 struct_field("label", "start"),
1071 struct_field("enabled", false),
1072 ],
1073 )
1074 .expect("struct instance");
1075
1076 mixed
1077 .set_field("count", 11_i64)
1078 .expect("update count field");
1079 assert_eq!(mixed.field::<i64>("count").expect("count field"), 11);
1080
1081 let err = mixed
1082 .set_field("count", "oops")
1083 .expect_err("type mismatch should fail");
1084 match err {
1085 LustError::TypeError { message } => {
1086 assert!(message.contains("count"));
1087 assert!(message.contains("int"));
1088 }
1089 other => panic!("unexpected error: {other:?}"),
1090 }
1091 assert_eq!(mixed.field::<i64>("count").expect("count field"), 11);
1092
1093 mixed
1094 .set_field("label", String::from("updated"))
1095 .expect("update label");
1096 assert_eq!(
1097 mixed.field::<String>("label").expect("label field"),
1098 "updated"
1099 );
1100
1101 mixed.set_field("enabled", true).expect("update enabled");
1102 assert!(mixed.field::<bool>("enabled").expect("enabled field"));
1103 }
1104
1105 #[test]
1106 fn struct_instance_accepts_nested_structs() {
1107 let _guard = serial_guard();
1108 let source = r#"
1109 struct Child
1110 value: int
1111 end
1112
1113 struct Parent
1114 child: main.Child
1115 end
1116 "#;
1117
1118 let program = build_program(source);
1119 let child = program
1120 .struct_instance("main.Child", [struct_field("value", 42_i64)])
1121 .expect("child struct");
1122 let parent = program
1123 .struct_instance("main.Parent", [struct_field("child", child.clone())])
1124 .expect("parent struct");
1125
1126 let nested: StructInstance = parent.field("child").expect("child field");
1127 assert_eq!(nested.field::<i64>("value").expect("value field"), 42);
1128 }
1129
1130 #[test]
1131 fn struct_handle_allows_field_mutation() {
1132 let _guard = serial_guard();
1133 let source = r#"
1134 struct Counter
1135 value: int
1136 end
1137 "#;
1138
1139 let program = build_program(source);
1140 let counter = program
1141 .struct_instance("main.Counter", [struct_field("value", 1_i64)])
1142 .expect("counter struct");
1143 let handle = counter.to_handle();
1144
1145 handle
1146 .set_field("value", 7_i64)
1147 .expect("update through handle");
1148 assert_eq!(handle.field::<i64>("value").expect("value field"), 7);
1149 assert_eq!(counter.field::<i64>("value").expect("value field"), 7);
1150
1151 handle
1152 .update_field("value", |current| match current {
1153 Value::Int(v) => Ok(v + 1),
1154 other => Err(LustError::RuntimeError {
1155 message: format!("unexpected value {other:?}"),
1156 }),
1157 })
1158 .expect("increment value");
1159 assert_eq!(counter.field::<i64>("value").expect("value field"), 8);
1160 }
1161
1162 #[test]
1163 fn value_ref_can_materialize_struct_handle() {
1164 let _guard = serial_guard();
1165 let source = r#"
1166 struct Child
1167 value: int
1168 end
1169
1170 struct Parent
1171 child: main.Child
1172 end
1173 "#;
1174
1175 let program = build_program(source);
1176 let child = program
1177 .struct_instance("main.Child", [struct_field("value", 10_i64)])
1178 .expect("child struct");
1179 let parent = program
1180 .struct_instance("main.Parent", [struct_field("child", child)])
1181 .expect("parent struct");
1182
1183 let handle = {
1184 let child_ref = parent.borrow_field("child").expect("child field borrow");
1185 child_ref
1186 .as_struct_handle()
1187 .expect("struct handle from value ref")
1188 };
1189 handle
1190 .set_field("value", 55_i64)
1191 .expect("update nested value");
1192
1193 let nested = parent
1194 .field::<StructInstance>("child")
1195 .expect("child field");
1196 assert_eq!(nested.field::<i64>("value").expect("value field"), 55);
1197 }
1198
1199 #[derive(crate::LustStructView)]
1200 #[lust(type = "main.Child", crate = "crate")]
1201 struct ChildView<'a> {
1202 #[lust(field = "value")]
1203 value: ValueRef<'a>,
1204 }
1205
1206 #[derive(crate::LustStructView)]
1207 #[lust(type = "main.Parent", crate = "crate")]
1208 struct ParentView<'a> {
1209 #[lust(field = "child")]
1210 child: StructHandle,
1211 #[lust(field = "label")]
1212 label: StringRef<'a>,
1213 }
1214
1215 #[test]
1216 fn derive_struct_view_zero_copy() {
1217 let _guard = serial_guard();
1218 let source = r#"
1219 struct Child
1220 value: int
1221 end
1222
1223 struct Parent
1224 child: main.Child
1225 label: string
1226 end
1227 "#;
1228
1229 let program = build_program(source);
1230 let child = program
1231 .struct_instance("main.Child", [struct_field("value", 7_i64)])
1232 .expect("child struct");
1233 let parent = program
1234 .struct_instance(
1235 "main.Parent",
1236 [
1237 struct_field("child", child.clone()),
1238 struct_field("label", "parent label"),
1239 ],
1240 )
1241 .expect("parent struct");
1242
1243 let handle = parent.to_handle();
1244 let view = ParentView::from_handle(&handle).expect("construct view");
1245 assert_eq!(view.child.field::<i64>("value").expect("child value"), 7);
1246 let label_rc_from_view = view.label.as_rc();
1247 assert_eq!(&*label_rc_from_view, "parent label");
1248
1249 let label_ref = parent.borrow_field("label").expect("borrow label");
1250 let label_rc = label_ref.as_rc_string().expect("label rc");
1251 assert!(Rc::ptr_eq(&label_rc_from_view, &label_rc));
1252
1253 let child_view = ChildView::from_handle(&view.child).expect("child view");
1254 assert_eq!(child_view.value.as_int().expect("child value"), 7);
1255
1256 match ParentView::from_handle(&child.to_handle()) {
1257 Ok(_) => panic!("expected type mismatch"),
1258 Err(LustError::TypeError { message }) => {
1259 assert!(message.contains("Parent"), "unexpected message: {message}");
1260 }
1261 Err(other) => panic!("unexpected error: {other:?}"),
1262 }
1263 }
1264
1265 #[test]
1266 fn globals_snapshot_exposes_lust_values() {
1267 let _guard = serial_guard();
1268 let source = r#"
1269 struct Child
1270 value: int
1271 end
1272
1273 struct Parent
1274 child: unknown
1275 end
1276
1277 function make_parent(): Parent
1278 return Parent { child = Child { value = 3 } }
1279 end
1280 "#;
1281
1282 let mut program = build_program(source);
1283 program.run_entry_script().expect("run entry script");
1284 let parent: StructInstance = program
1285 .call_typed("main.make_parent", ())
1286 .expect("call make_parent");
1287 program.set_global_value("main.some_nested_structure", parent.clone());
1288
1289 let globals = program.globals();
1290 let (_, value) = globals
1291 .into_iter()
1292 .find(|(name, _)| name.ends_with("some_nested_structure"))
1293 .expect("global binding present");
1294 let stored =
1295 <StructInstance as FromLustValue>::from_value(value).expect("convert to struct");
1296 let child_value = stored
1297 .field::<StructInstance>("child")
1298 .expect("nested child");
1299 assert_eq!(child_value.field::<i64>("value").expect("child value"), 3);
1300 }
1301
1302 #[test]
1303 fn function_handle_supports_typed_and_raw_calls() {
1304 let _guard = serial_guard();
1305 let source = r#"
1306 pub function add(a: int, b: int): int
1307 return a + b
1308 end
1309 "#;
1310
1311 let mut program = build_program(source);
1312 let handle = program
1313 .function_handle("main.add")
1314 .expect("function handle");
1315
1316 let typed: i64 = handle
1317 .call_typed(&mut program, (2_i64, 3_i64))
1318 .expect("typed call");
1319 assert_eq!(typed, 5);
1320
1321 let raw = handle
1322 .call_raw(&mut program, vec![Value::Int(4_i64), Value::Int(6_i64)])
1323 .expect("raw call");
1324 assert_eq!(raw.as_int(), Some(10));
1325
1326 let signature = program.signature("main.add").expect("signature").clone();
1327 handle
1328 .validate_signature(&program, &signature)
1329 .expect("matching signature");
1330
1331 let mut mismatched = signature.clone();
1332 mismatched.return_type = Type::new(TypeKind::Bool, Span::new(0, 0, 0, 0));
1333 assert!(
1334 handle.validate_signature(&program, &mismatched).is_err(),
1335 "expected signature mismatch"
1336 );
1337 }
1338
1339 #[test]
1340 fn async_task_native_returns_task_handle() {
1341 let _guard = serial_guard();
1342 let source = r#"
1343 extern
1344 function fetch_value(): Task
1345 end
1346
1347 pub function start(): Task
1348 return fetch_value()
1349 end
1350 "#;
1351
1352 let mut program = build_program(source);
1353 program
1354 .register_async_task_native::<(), LustInt, _, _>("fetch_value", move |_| async move {
1355 Ok(42_i64)
1356 })
1357 .expect("register async task native");
1358
1359 let task_value = program
1360 .call_raw("main.start", Vec::new())
1361 .expect("call start");
1362 let handle = match task_value {
1363 Value::Task(handle) => handle,
1364 other => panic!("expected task handle, found {other:?}"),
1365 };
1366
1367 {
1368 let mut driver = AsyncDriver::new(&mut program);
1369 driver.pump_until_idle().expect("poll async");
1370 }
1371
1372 let (state_label, last_result, err) = {
1373 let vm = program.vm_mut();
1374 let task = vm.get_task_instance(handle).expect("task instance");
1375 (
1376 task.state.as_str().to_string(),
1377 task.last_result.clone(),
1378 task.error.clone(),
1379 )
1380 };
1381 assert_eq!(state_label, "completed");
1382 assert!(err.is_none());
1383 let int_value = last_result
1384 .and_then(|value| value.as_int())
1385 .expect("int result");
1386 assert_eq!(int_value, 42);
1387 }
1388
1389 #[test]
1390 fn rust_declared_struct_and_static_method_are_available() {
1391 let _guard = serial_guard();
1392 let struct_def = StructBuilder::new("math.Point")
1393 .field(struct_field_decl(
1394 "x",
1395 Type::new(TypeKind::Int, Span::dummy()),
1396 ))
1397 .field(struct_field_decl(
1398 "y",
1399 Type::new(TypeKind::Int, Span::dummy()),
1400 ))
1401 .finish();
1402 let origin_fn = FunctionBuilder::new("math.origin_x")
1403 .return_type(Type::new(TypeKind::Int, Span::dummy()))
1404 .finish();
1405
1406 let module = r#"
1407 pub function make(): math.Point
1408 return math.Point { x = 10, y = 20 }
1409 end
1410 "#;
1411
1412 let mut program = EmbeddedProgram::builder()
1413 .module("main", module)
1414 .entry_module("main")
1415 .declare_struct(struct_def)
1416 .declare_function(origin_fn)
1417 .compile()
1418 .expect("compile program");
1419
1420 let point_value = program
1421 .call_raw("main.make", Vec::new())
1422 .expect("call make");
1423 match point_value {
1424 Value::Struct { name, .. } => assert_eq!(name, "math.Point"),
1425 other => panic!("expected struct value, found {other:?}"),
1426 }
1427
1428 program
1429 .register_typed_native::<(), LustInt, _>("math.origin_x", |_| Ok(123_i64))
1430 .expect("register static origin");
1431 let signature = program.signature("math.origin_x");
1432 assert!(signature.is_some());
1433 }
1434
1435 #[test]
1436 fn update_field_modifies_value_in_place() {
1437 let _guard = serial_guard();
1438 let source = r#"
1439 struct Counter
1440 value: int
1441 end
1442 "#;
1443
1444 let program = build_program(source);
1445 let counter = program
1446 .struct_instance("main.Counter", [struct_field("value", 10_i64)])
1447 .expect("counter struct");
1448
1449 counter
1450 .update_field("value", |current| match current {
1451 Value::Int(v) => Ok(v + 5),
1452 other => Err(LustError::RuntimeError {
1453 message: format!("unexpected value {other:?}"),
1454 }),
1455 })
1456 .expect("update in place");
1457 assert_eq!(counter.field::<i64>("value").expect("value field"), 15);
1458
1459 let err = counter
1460 .update_field("value", |_| Ok(String::from("oops")))
1461 .expect_err("string should fail type check");
1462 match err {
1463 LustError::TypeError { message } => {
1464 assert!(message.contains("value"));
1465 assert!(message.contains("int"));
1466 }
1467 other => panic!("unexpected error: {other:?}"),
1468 }
1469 assert_eq!(counter.field::<i64>("value").expect("value field"), 15);
1470
1471 let err = counter
1472 .update_field("value", |_| -> Result<i64> {
1473 Err(LustError::RuntimeError {
1474 message: "closure failure".to_string(),
1475 })
1476 })
1477 .expect_err("closure error should propagate");
1478 match err {
1479 LustError::RuntimeError { message } => assert_eq!(message, "closure failure"),
1480 other => panic!("unexpected error: {other:?}"),
1481 }
1482 assert_eq!(counter.field::<i64>("value").expect("value field"), 15);
1483 }
1484}