datex_core/references/
reference.rs

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