Skip to main content

datex_core/references/
reference.rs

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