datex_core/references/
reference.rs

1use crate::references::type_reference::{
2    NominalTypeDeclaration, TypeReference,
3};
4use crate::values::core_value::CoreValue;
5use core::prelude::rust_2024::*;
6use core::result::Result;
7
8use crate::references::value_reference::ValueReference;
9use crate::runtime::execution::ExecutionError;
10use crate::stdlib::boxed::Box;
11use crate::stdlib::rc::Rc;
12use crate::stdlib::string::String;
13use crate::stdlib::string::ToString;
14use crate::traits::apply::Apply;
15use crate::traits::identity::Identity;
16use crate::traits::structural_eq::StructuralEq;
17use crate::traits::value_eq::ValueEq;
18use crate::types::definition::TypeDefinition;
19use crate::values::core_values::map::MapAccessError;
20use crate::values::core_values::r#type::Type;
21use crate::values::pointer::PointerAddress;
22use crate::values::value::Value;
23use crate::values::value_container::{ValueContainer, ValueKey};
24use core::cell::RefCell;
25use core::fmt::Display;
26use core::hash::{Hash, Hasher};
27use core::ops::FnOnce;
28use core::option::Option;
29use core::unreachable;
30use core::write;
31use num_enum::TryFromPrimitive;
32use serde::{Deserialize, Serialize};
33
34#[derive(Debug)]
35pub struct IndexOutOfBoundsError {
36    pub index: u32,
37}
38
39impl Display for IndexOutOfBoundsError {
40    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
41        write!(f, "Index out of bounds: {}", self.index)
42    }
43}
44
45#[derive(Debug, Clone, PartialEq)]
46pub struct KeyNotFoundError {
47    pub key: ValueContainer,
48}
49
50impl Display for KeyNotFoundError {
51    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
52        write!(f, "Property not found: {}", self.key)
53    }
54}
55
56#[derive(Debug)]
57pub enum AccessError {
58    ImmutableReference,
59    InvalidOperation(String),
60    KeyNotFound(KeyNotFoundError),
61    IndexOutOfBounds(IndexOutOfBoundsError),
62    MapAccessError(MapAccessError),
63    InvalidIndexKey,
64}
65
66impl From<IndexOutOfBoundsError> for AccessError {
67    fn from(err: IndexOutOfBoundsError) -> Self {
68        AccessError::IndexOutOfBounds(err)
69    }
70}
71
72impl From<MapAccessError> for AccessError {
73    fn from(err: MapAccessError) -> Self {
74        AccessError::MapAccessError(err)
75    }
76}
77
78impl From<KeyNotFoundError> for AccessError {
79    fn from(err: KeyNotFoundError) -> Self {
80        AccessError::KeyNotFound(err)
81    }
82}
83
84impl Display for AccessError {
85    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
86        match self {
87            AccessError::MapAccessError(err) => {
88                write!(f, "Map access error: {}", err)
89            }
90            AccessError::ImmutableReference => {
91                write!(f, "Cannot modify an immutable reference")
92            }
93            AccessError::InvalidOperation(op) => {
94                write!(f, "Invalid operation: {}", op)
95            }
96            AccessError::KeyNotFound(key) => {
97                write!(f, "{}", key)
98            }
99            AccessError::IndexOutOfBounds(error) => {
100                write!(f, "{}", error)
101            }
102            AccessError::InvalidIndexKey => {
103                write!(f, "Invalid index key")
104            }
105        }
106    }
107}
108
109#[derive(Debug)]
110pub enum TypeError {
111    TypeMismatch { expected: Type, found: Type },
112}
113impl Display for TypeError {
114    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
115        match self {
116            TypeError::TypeMismatch { expected, found } => write!(
117                f,
118                "Type mismatch: expected {}, found {}",
119                expected, found
120            ),
121        }
122    }
123}
124
125#[derive(Debug)]
126pub enum AssignmentError {
127    ImmutableReference,
128    TypeError(Box<TypeError>),
129}
130
131impl Display for AssignmentError {
132    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
133        match self {
134            AssignmentError::ImmutableReference => {
135                write!(f, "Cannot assign to an immutable reference")
136            }
137            AssignmentError::TypeError(e) => {
138                write!(f, "Type error: {}", e)
139            }
140        }
141    }
142}
143
144#[derive(
145    Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, TryFromPrimitive,
146)]
147#[repr(u8)]
148pub enum ReferenceMutability {
149    Mutable = 0,
150    Immutable = 1,
151}
152
153pub mod mutability_as_int {
154    use super::ReferenceMutability;
155    use crate::stdlib::format;
156    use core::prelude::rust_2024::*;
157    use serde::de::Error;
158    use serde::{Deserialize, Deserializer, Serializer};
159
160    pub fn serialize<S>(
161        value: &ReferenceMutability,
162        serializer: S,
163    ) -> Result<S::Ok, S::Error>
164    where
165        S: Serializer,
166    {
167        match value {
168            (ReferenceMutability::Mutable) => serializer.serialize_u8(0),
169            (ReferenceMutability::Immutable) => serializer.serialize_u8(1),
170        }
171    }
172
173    pub fn deserialize<'de, D>(
174        deserializer: D,
175    ) -> Result<ReferenceMutability, D::Error>
176    where
177        D: Deserializer<'de>,
178    {
179        let opt = u8::deserialize(deserializer)?;
180        Ok(match opt {
181            (0) => (ReferenceMutability::Mutable),
182            (1) => (ReferenceMutability::Immutable),
183            (x) => {
184                return Err(D::Error::custom(format!(
185                    "invalid mutability code: {}",
186                    x
187                )));
188            }
189        })
190    }
191}
192pub mod mutability_option_as_int {
193    use super::ReferenceMutability;
194    use crate::stdlib::format;
195    use core::prelude::rust_2024::*;
196    use serde::de::Error;
197    use serde::{Deserialize, Deserializer, Serializer};
198
199    pub fn serialize<S>(
200        value: &Option<ReferenceMutability>,
201        serializer: S,
202    ) -> Result<S::Ok, S::Error>
203    where
204        S: Serializer,
205    {
206        match value {
207            Some(ReferenceMutability::Mutable) => serializer.serialize_u8(0),
208            Some(ReferenceMutability::Immutable) => serializer.serialize_u8(1),
209            None => serializer.serialize_none(),
210        }
211    }
212
213    pub fn deserialize<'de, D>(
214        deserializer: D,
215    ) -> Result<Option<ReferenceMutability>, D::Error>
216    where
217        D: Deserializer<'de>,
218    {
219        let opt = Option::<u8>::deserialize(deserializer)?;
220        Ok(match opt {
221            Some(0) => Some(ReferenceMutability::Mutable),
222            Some(1) => Some(ReferenceMutability::Immutable),
223            Some(x) => {
224                return Err(D::Error::custom(format!(
225                    "invalid mutability code: {}",
226                    x
227                )));
228            }
229            None => None,
230        })
231    }
232}
233
234impl Display for ReferenceMutability {
235    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
236        match self {
237            ReferenceMutability::Mutable => write!(f, "&mut"),
238            ReferenceMutability::Immutable => write!(f, "&"),
239        }
240    }
241}
242
243#[derive(Debug, Clone)]
244pub enum Reference {
245    ValueReference(Rc<RefCell<ValueReference>>),
246    TypeReference(Rc<RefCell<TypeReference>>),
247}
248
249impl Display for Reference {
250    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
251        match self {
252            Reference::ValueReference(vr) => {
253                let vr = vr.borrow();
254                write!(f, "{} {}", vr.mutability, vr.value_container)
255            }
256            Reference::TypeReference(tr) => {
257                let tr = tr.borrow();
258                write!(f, "{}", tr)
259            }
260        }
261    }
262}
263
264impl From<ValueReference> for Reference {
265    fn from(reference: ValueReference) -> Self {
266        Reference::ValueReference(Rc::new(RefCell::new(reference)))
267    }
268}
269impl From<TypeReference> for Reference {
270    fn from(reference: TypeReference) -> Self {
271        Reference::TypeReference(Rc::new(RefCell::new(reference)))
272    }
273}
274
275/// Two references are identical if they point to the same data
276impl Identity for Reference {
277    fn identical(&self, other: &Self) -> bool {
278        match (self, other) {
279            (Reference::ValueReference(a), Reference::ValueReference(b)) => {
280                Rc::ptr_eq(a, b)
281            }
282            (Reference::TypeReference(a), Reference::TypeReference(b)) => {
283                Rc::ptr_eq(a, b)
284            }
285            _ => false,
286        }
287    }
288}
289
290impl Eq for Reference {}
291
292/// PartialEq corresponds to pointer equality / identity for `Reference`.
293impl PartialEq for Reference {
294    fn eq(&self, other: &Self) -> bool {
295        self.identical(other)
296    }
297}
298
299impl StructuralEq for Reference {
300    fn structural_eq(&self, other: &Self) -> bool {
301        match (self, other) {
302            (Reference::TypeReference(a), Reference::TypeReference(b)) => {
303                a.borrow().type_value.structural_eq(&b.borrow().type_value)
304            }
305            (Reference::ValueReference(a), Reference::ValueReference(b)) => a
306                .borrow()
307                .value_container
308                .structural_eq(&b.borrow().value_container),
309            _ => false,
310        }
311    }
312}
313
314impl ValueEq for Reference {
315    fn value_eq(&self, other: &Self) -> bool {
316        match (self, other) {
317            (Reference::TypeReference(a), Reference::TypeReference(b)) => {
318                // FIXME #281: Implement value_eq for type and use here instead (recursive)
319                a.borrow().type_value.structural_eq(&b.borrow().type_value)
320            }
321            (Reference::ValueReference(a), Reference::ValueReference(b)) => a
322                .borrow()
323                .value_container
324                .value_eq(&b.borrow().value_container),
325            _ => false,
326        }
327    }
328}
329
330impl Hash for Reference {
331    fn hash<H: Hasher>(&self, state: &mut H) {
332        match self {
333            Reference::TypeReference(tr) => {
334                let ptr = Rc::as_ptr(tr);
335                ptr.hash(state); // hash the address
336            }
337            Reference::ValueReference(vr) => {
338                let ptr = Rc::as_ptr(vr);
339                ptr.hash(state); // hash the address
340            }
341        }
342    }
343}
344
345impl<T: Into<ValueContainer>> From<T> for Reference {
346    /// Creates a new immutable reference from a value container.
347    fn from(value_container: T) -> Self {
348        let value_container = value_container.into();
349        Reference::try_new_from_value_container(
350            value_container,
351            None,
352            None,
353            ReferenceMutability::Immutable,
354        )
355        .unwrap()
356    }
357}
358
359#[derive(Debug, Clone, PartialEq, Eq, Hash)]
360pub enum ReferenceCreationError {
361    InvalidType,
362    MutableTypeReference,
363}
364
365impl Display for ReferenceCreationError {
366    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
367        match self {
368            ReferenceCreationError::InvalidType => {
369                write!(
370                    f,
371                    "Cannot create reference from value container: invalid type"
372                )
373            }
374            ReferenceCreationError::MutableTypeReference => {
375                write!(f, "Cannot create mutable reference for type")
376            }
377        }
378    }
379}
380
381impl Reference {
382    /// Runs a closure with the current value of this reference.
383    pub(crate) fn with_value<R, F: FnOnce(&mut Value) -> R>(
384        &self,
385        f: F,
386    ) -> Option<R> {
387        let reference = self.collapse_reference_chain();
388        match reference {
389            Reference::ValueReference(vr) => {
390                match &mut vr.borrow_mut().value_container {
391                    ValueContainer::Value(value) => Some(f(value)),
392                    ValueContainer::Reference(_) => {
393                        unreachable!(
394                            "Expected a ValueContainer::Value, but found a Reference"
395                        )
396                    }
397                }
398            }
399            Reference::TypeReference(_) => None,
400        }
401    }
402
403    // TODO #282: Mark as unsafe function
404    /// Note: borrows the contained value. While in callback, no other borrows to the value are allowed.
405    pub(crate) fn with_value_unchecked<R, F: FnOnce(&mut Value) -> R>(
406        &self,
407        f: F,
408    ) -> R {
409        unsafe { self.with_value(f).unwrap_unchecked() }
410    }
411}
412
413impl Reference {
414    pub fn pointer_address(&self) -> Option<PointerAddress> {
415        match self {
416            Reference::ValueReference(vr) => {
417                vr.borrow().pointer_address().clone()
418            }
419            Reference::TypeReference(tr) => tr.borrow().pointer_address.clone(),
420        }
421    }
422
423    /// Sets the pointer address of the reference.
424    /// Panics if the reference already has a pointer address.
425    pub fn set_pointer_address(&self, pointer_address: PointerAddress) {
426        if self.pointer_address().is_some() {
427            core::panic!(
428                "Cannot set pointer address on reference that already has one"
429            );
430        }
431        match self {
432            Reference::ValueReference(vr) => {
433                vr.borrow_mut().pointer_address = Some(pointer_address)
434            }
435            Reference::TypeReference(tr) => {
436                tr.borrow_mut().pointer_address = Some(pointer_address)
437            }
438        }
439    }
440
441    /// Gets the mutability of the reference.
442    /// TypeReferences are always immutable.
443    pub(crate) fn mutability(&self) -> ReferenceMutability {
444        match self {
445            Reference::ValueReference(vr) => vr.borrow().mutability.clone(),
446            Reference::TypeReference(_) => ReferenceMutability::Immutable,
447        }
448    }
449
450    /// Checks if the reference is mutable.
451    /// A reference is mutable if it is a mutable ValueReference and all references in the chain are mutable.
452    /// TypeReferences are always immutable.
453    /// FIXME #284: Do we really need this? Probably we already collapse the ref and then change it's value and perform
454    /// the mutability check on the most inner ref.
455    pub fn is_mutable(&self) -> bool {
456        match self {
457            Reference::TypeReference(_) => false, // type references are always immutable
458            Reference::ValueReference(vr) => {
459                let vr_borrow = vr.borrow();
460                // if the current reference is immutable, whole chain is immutable
461                if vr_borrow.mutability != ReferenceMutability::Mutable {
462                    return false;
463                }
464
465                // otherwise, check if ref is pointing to another reference
466                match &vr_borrow.value_container {
467                    ValueContainer::Reference(inner) => inner.is_mutable(),
468                    ValueContainer::Value(_) => true,
469                }
470            }
471        }
472    }
473
474    /// Creates a new reference from a value container
475    pub fn try_new_from_value_container(
476        value_container: ValueContainer,
477        allowed_type: Option<TypeDefinition>,
478        maybe_pointer_id: Option<PointerAddress>,
479        mutability: ReferenceMutability,
480    ) -> Result<Self, ReferenceCreationError> {
481        // FIXME #285 implement type check
482        Ok(match value_container {
483            ValueContainer::Reference(ref reference) => {
484                match reference {
485                    Reference::ValueReference(vr) => {
486                        let allowed_type = allowed_type.unwrap_or_else(|| {
487                            vr.borrow().allowed_type.clone()
488                        });
489                        // TODO #286: make sure allowed type is superset of reference's allowed type
490                        Reference::ValueReference(Rc::new(RefCell::new(
491                            ValueReference::new(
492                                value_container,
493                                maybe_pointer_id,
494                                allowed_type,
495                                mutability,
496                            ),
497                        )))
498                    }
499                    Reference::TypeReference(tr) => {
500                        if mutability == ReferenceMutability::Mutable {
501                            return Err(
502                                ReferenceCreationError::MutableTypeReference,
503                            );
504                        }
505                        Reference::TypeReference(
506                            TypeReference::anonymous(
507                                Type::reference(tr.clone(), mutability),
508                                maybe_pointer_id,
509                            )
510                            .as_ref_cell(),
511                        )
512                    }
513                }
514            }
515            ValueContainer::Value(value) => {
516                match value.inner {
517                    // create TypeReference if the value is a Type
518                    CoreValue::Type(type_value) => {
519                        // TODO #287: allowed_type "Type" is also allowed
520                        if allowed_type.is_some() {
521                            return Err(ReferenceCreationError::InvalidType);
522                        }
523                        if mutability == ReferenceMutability::Mutable {
524                            return Err(
525                                ReferenceCreationError::MutableTypeReference,
526                            );
527                        }
528                        Reference::new_from_type(
529                            type_value,
530                            maybe_pointer_id,
531                            None,
532                        )
533                    }
534                    // otherwise create ValueReference
535                    _ => {
536                        let allowed_type = allowed_type.unwrap_or_else(|| {
537                            value.actual_type.as_ref().clone()
538                        });
539                        Reference::ValueReference(Rc::new(RefCell::new(
540                            ValueReference::new(
541                                ValueContainer::Value(value),
542                                maybe_pointer_id,
543                                allowed_type,
544                                mutability,
545                            ),
546                        )))
547                    }
548                }
549            }
550        })
551    }
552
553    pub fn new_from_type(
554        type_value: Type,
555        maybe_pointer_address: Option<PointerAddress>,
556        maybe_nominal_type_declaration: Option<NominalTypeDeclaration>,
557    ) -> Self {
558        let type_reference = TypeReference {
559            pointer_address: maybe_pointer_address,
560            nominal_type_declaration: maybe_nominal_type_declaration,
561            type_value,
562        };
563        Reference::TypeReference(Rc::new(RefCell::new(type_reference)))
564    }
565
566    pub fn try_mut_from(
567        value_container: ValueContainer,
568    ) -> Result<Self, ReferenceCreationError> {
569        Reference::try_new_from_value_container(
570            value_container,
571            None,
572            None,
573            ReferenceMutability::Mutable,
574        )
575    }
576
577    /// Collapses the reference chain to most inner reference to which this reference points.
578    pub fn collapse_reference_chain(&self) -> Reference {
579        match self {
580            // FIXME #288: Can we optimize this to avoid creating rc ref cells?
581            Reference::TypeReference(tr) => Reference::TypeReference(Rc::new(
582                RefCell::new(tr.borrow().collapse_reference_chain()),
583            )),
584            Reference::ValueReference(vr) => {
585                match &vr.borrow().value_container {
586                    ValueContainer::Reference(reference) => {
587                        // If this is a reference, resolve it to its current value
588                        reference.collapse_reference_chain()
589                    }
590                    ValueContainer::Value(_) => {
591                        // If this is a value, return it directly
592                        self.clone()
593                    }
594                }
595            }
596        }
597    }
598
599    /// Converts a reference to its current value, collapsing any reference chains and converting type references to type values.
600    pub fn collapse_to_value(&self) -> Rc<RefCell<Value>> {
601        let reference = self.collapse_reference_chain();
602        match reference {
603            Reference::ValueReference(vr) => match &vr.borrow().value_container
604            {
605                ValueContainer::Value(_) => {
606                    vr.borrow().value_container.to_value()
607                }
608                ValueContainer::Reference(_) => unreachable!(
609                    "Expected a ValueContainer::Value, but found a Reference"
610                ),
611            },
612            // TODO #289: can we optimize this to avoid cloning the type value?
613            Reference::TypeReference(tr) => Rc::new(RefCell::new(Value::from(
614                CoreValue::Type(tr.borrow().type_value.clone()),
615            ))),
616        }
617    }
618
619    // TODO #290: no clone?
620    pub fn value_container(&self) -> ValueContainer {
621        match self {
622            Reference::ValueReference(vr) => {
623                vr.borrow().value_container.clone()
624            }
625            Reference::TypeReference(tr) => ValueContainer::Value(Value::from(
626                CoreValue::Type(tr.borrow().type_value.clone()),
627            )),
628        }
629    }
630
631    pub fn allowed_type(&self) -> TypeDefinition {
632        match self {
633            Reference::ValueReference(vr) => vr.borrow().allowed_type.clone(),
634            Reference::TypeReference(_) => core::todo!("#293 type Type"),
635        }
636    }
637
638    pub fn actual_type(&self) -> TypeDefinition {
639        match self {
640            Reference::ValueReference(vr) => vr
641                .borrow()
642                .value_container
643                .to_value()
644                .borrow()
645                .actual_type()
646                .clone(),
647            Reference::TypeReference(tr) => core::todo!("#294 type Type"),
648        }
649    }
650
651    pub fn is_type(&self) -> bool {
652        match self {
653            Reference::TypeReference(_) => true,
654            Reference::ValueReference(vr) => {
655                vr.borrow().resolve_current_value().borrow().is_type()
656            }
657        }
658    }
659
660    /// Returns a mutable reference to the ValueReference if this is a mutable ValueReference.
661    pub fn mutable_reference(&self) -> Option<Rc<RefCell<ValueReference>>> {
662        match self {
663            Reference::TypeReference(_) => None,
664            Reference::ValueReference(vr) => {
665                if vr.borrow().is_mutable() {
666                    Some(vr.clone())
667                } else {
668                    None
669                }
670            }
671        }
672    }
673
674    /// Sets the value container of the reference if it is mutable.
675    /// If the reference is immutable, an error is returned.
676    pub fn set_value_container(
677        &self,
678        new_value_container: ValueContainer,
679    ) -> Result<(), AssignmentError> {
680        match &self {
681            Reference::TypeReference(_) => {
682                Err(AssignmentError::ImmutableReference)
683            }
684            Reference::ValueReference(vr) => {
685                if self.is_mutable() {
686                    // TODO #295: check type compatibility, handle observers
687                    vr.borrow_mut().value_container = new_value_container;
688                    Ok(())
689                } else {
690                    Err(AssignmentError::ImmutableReference)
691                }
692            }
693        }
694    }
695}
696/// Getter for references
697impl Reference {
698    /// Gets a property on the value if applicable (e.g. for map and structs)
699    // FIXME #296 make this return a reference to a value container
700    // Just for later as myRef.x += 1
701    // key_ref = myRef.x // myRef.try_get_property("x".into())
702    // key_val = &key_ref.value()
703    // &key_ref.set_value(key_val + 1)
704    // -> we could avoid some clones if so (as get, addition, set would all be a clone)
705    pub fn try_get_property<'a>(
706        &self,
707        key: impl Into<ValueKey<'a>>,
708    ) -> Result<ValueContainer, AccessError> {
709        self.with_value(|value| value.try_get_property(key))
710            .unwrap_or(Err(AccessError::InvalidOperation(
711                "Cannot get property on invalid reference".to_string(),
712            )))
713    }
714}
715
716impl Apply for Reference {
717    fn apply(
718        &self,
719        args: &[ValueContainer],
720    ) -> Result<Option<ValueContainer>, ExecutionError> {
721        match self {
722            Reference::TypeReference(tr) => tr.borrow().apply(args),
723            Reference::ValueReference(vr) => {
724                vr.borrow().resolve_current_value().borrow().apply(args)
725            }
726        }
727    }
728
729    fn apply_single(
730        &self,
731        arg: &ValueContainer,
732    ) -> Result<Option<ValueContainer>, ExecutionError> {
733        match self {
734            Reference::TypeReference(tr) => tr.borrow().apply_single(arg),
735            Reference::ValueReference(vr) => vr
736                .borrow()
737                .resolve_current_value()
738                .borrow()
739                .apply_single(arg),
740        }
741    }
742}
743
744#[cfg(test)]
745mod tests {
746    use super::*;
747    use crate::runtime::global_context::{GlobalContext, set_global_context};
748    use crate::runtime::memory::Memory;
749    use crate::stdlib::assert_matches::assert_matches;
750    use crate::traits::value_eq::ValueEq;
751    use crate::{assert_identical, assert_structural_eq, assert_value_eq};
752    use datex_core::values::core_values::map::Map;
753
754    #[test]
755    fn try_mut_from() {
756        // creating a mutable reference from a value should work
757        let value = ValueContainer::from(42);
758        let reference = Reference::try_mut_from(value).unwrap();
759        assert_eq!(reference.mutability(), ReferenceMutability::Mutable);
760
761        // creating a mutable reference from a type should fail
762        let type_value = ValueContainer::Reference(Reference::TypeReference(
763            TypeReference::anonymous(Type::UNIT, None).as_ref_cell(),
764        ));
765        assert_matches!(
766            Reference::try_mut_from(type_value),
767            Err(ReferenceCreationError::MutableTypeReference)
768        );
769    }
770
771    #[test]
772    fn property() {
773        let mut map = Map::default();
774        map.set("name", ValueContainer::from("Jonas"));
775        map.set("age", ValueContainer::from(30));
776        let reference = Reference::from(ValueContainer::from(map));
777        assert_eq!(
778            reference.try_get_property("name").unwrap(),
779            ValueContainer::from("Jonas")
780        );
781        assert_eq!(
782            reference.try_get_property("age").unwrap(),
783            ValueContainer::from(30)
784        );
785        assert!(reference.try_get_property("nonexistent").is_err());
786        assert_matches!(
787            reference.try_get_property("nonexistent"),
788            Err(AccessError::KeyNotFound(_))
789        );
790    }
791
792    #[test]
793    fn text_property() {
794        let struct_val = Map::from(vec![
795            ("name".to_string(), ValueContainer::from("Jonas")),
796            ("age".to_string(), ValueContainer::from(30)),
797        ]);
798        let reference = Reference::from(ValueContainer::from(struct_val));
799        assert_eq!(
800            reference.try_get_property("name").unwrap(),
801            ValueContainer::from("Jonas")
802        );
803        assert_eq!(
804            reference.try_get_property("age").unwrap(),
805            ValueContainer::from(30)
806        );
807        assert!(reference.try_get_property("nonexistent").is_err());
808        assert_matches!(
809            reference.try_get_property("nonexistent"),
810            Err(AccessError::KeyNotFound(_))
811        );
812    }
813
814    #[test]
815    fn numeric_property() {
816        let list = vec![
817            ValueContainer::from(1),
818            ValueContainer::from(2),
819            ValueContainer::from(3),
820        ];
821        let reference = Reference::from(ValueContainer::from(list));
822
823        assert_eq!(
824            reference.try_get_property(0).unwrap(),
825            ValueContainer::from(1)
826        );
827        assert_eq!(
828            reference.try_get_property(1).unwrap(),
829            ValueContainer::from(2)
830        );
831        assert_eq!(
832            reference.try_get_property(2).unwrap(),
833            ValueContainer::from(3)
834        );
835        assert!(reference.try_get_property(3).is_err());
836
837        assert_matches!(
838            reference.try_get_property(100),
839            Err(AccessError::IndexOutOfBounds(IndexOutOfBoundsError {
840                index: 100
841            }))
842        );
843
844        let text_ref = Reference::from(ValueContainer::from("hello"));
845        assert_eq!(
846            text_ref.try_get_property(1).unwrap(),
847            ValueContainer::from("e".to_string())
848        );
849        assert!(text_ref.try_get_property(5).is_err());
850        assert_matches!(
851            text_ref.try_get_property(100),
852            Err(AccessError::IndexOutOfBounds(IndexOutOfBoundsError {
853                index: 100
854            }))
855        );
856    }
857
858    #[test]
859    fn reference_identity() {
860        let value = 42;
861        let reference1 = Reference::from(value);
862        let reference2 = reference1.clone();
863
864        // cloned reference should be equal (identical)
865        assert_eq!(reference1, reference2);
866        // value containers containing the references should also be equal
867        assert_eq!(
868            ValueContainer::Reference(reference1.clone()),
869            ValueContainer::Reference(reference2.clone())
870        );
871        // assert_identical! should also confirm identity
872        assert_identical!(reference1.clone(), reference2);
873        // separate reference containing the same value should not be equal
874        assert_ne!(reference1, Reference::from(value));
875    }
876
877    #[test]
878    fn reference_value_equality() {
879        let value = 42;
880        let reference1 = ValueContainer::Reference(Reference::from(value));
881        let reference2 = ValueContainer::Reference(Reference::from(value));
882
883        // different references should not be equal a.k.a. identical
884        assert_ne!(reference1, reference2);
885        // but their current resolved values should be equal
886        assert_value_eq!(reference1, ValueContainer::from(value));
887    }
888
889    #[test]
890    fn reference_structural_equality() {
891        let reference1 = Reference::from(42.0);
892        let reference2 = Reference::from(42);
893
894        // different references should not be equal a.k.a. identical
895        assert_ne!(reference1, reference2);
896        // but their current resolved values should be structurally equal
897        assert!(!reference1.structural_eq(&reference2));
898    }
899
900    #[test]
901    fn nested_references() {
902        set_global_context(GlobalContext::native());
903        let memory = &RefCell::new(Memory::default());
904
905        let mut map_a = Map::default();
906        map_a.set("number", ValueContainer::from(42));
907        map_a.set("obj", ValueContainer::new_reference(Map::default()));
908
909        // construct map_a as a value first
910        let map_a_original_ref = ValueContainer::new_reference(map_a);
911
912        // create map_b as a reference
913        let map_b_ref = Reference::try_new_from_value_container(
914            Map::default().into(),
915            None,
916            None,
917            ReferenceMutability::Mutable,
918        )
919        .unwrap();
920
921        // set map_a as property of b. This should create a reference to a clone of map_a that
922        // is upgraded to a reference
923        map_b_ref
924            .try_set_property(0, memory, "a", map_a_original_ref.clone())
925            .unwrap();
926
927        // assert that the reference to map_a is set correctly
928        let map_a_ref = map_b_ref.try_get_property("a").unwrap();
929        assert_structural_eq!(map_a_ref, map_a_original_ref);
930        assert_eq!(map_a_ref, map_a_original_ref);
931        assert_identical!(map_a_ref, map_a_original_ref);
932        // map_a_ref should be a reference
933        assert_matches!(map_a_ref, ValueContainer::Reference(_));
934        map_a_ref.with_maybe_reference(|a_ref| {
935            // map_a_ref.number should be a value
936            assert_matches!(
937                a_ref.try_get_property("number"),
938                Ok(ValueContainer::Value(_))
939            );
940            // map_a_ref.obj should be a reference
941            assert_matches!(
942                a_ref.try_get_property("obj"),
943                Ok(ValueContainer::Reference(_))
944            );
945        });
946    }
947}