Skip to main content

pax_runtime_api/
variables.rs

1//! Property adapters that expose runtime values to expression scopes.
2
3use super::*;
4
5#[cfg(test)]
6mod tests;
7
8/// Runtime property adapter used by expression scopes.
9#[derive(Clone)]
10pub struct Variable {
11    untyped_property: UntypedProperty,
12    converted_to_pax_value: Property<PaxValue>,
13    rust_type: std::any::TypeId,
14}
15
16impl Variable {
17    /// Wraps an untyped property and exposes it as a `PaxValue`.
18    pub fn new<T: PropertyValue + ToPaxValue>(untyped_property: UntypedProperty) -> Self {
19        Self::new_from_typed_property(Property::<T>::new_from_untyped(untyped_property.clone()))
20    }
21
22    /// Wraps a typed property and exposes it as a `PaxValue`.
23    pub fn new_from_typed_property<T: PropertyValue + ToPaxValue>(property: Property<T>) -> Self {
24        let untyped_property = property.untyped();
25        let deps = [untyped_property.clone()];
26        let pax_value_prop = Property::computed(move || property.get().to_pax_value(), &deps);
27        Variable {
28            untyped_property,
29            converted_to_pax_value: pax_value_prop,
30            rust_type: std::any::TypeId::of::<T>(),
31        }
32    }
33
34    /// Creates an independent one-way binding when exact typed forwarding is
35    /// safe. Unlike a double binding, writes/easing on the result never mutate
36    /// the source. Mismatched types and custom conversions return `None`.
37    pub fn try_typed_binding<T: PropertyValue + CoercionRules>(
38        &self,
39        name: &str,
40    ) -> Option<Property<T>> {
41        if self.rust_type != std::any::TypeId::of::<T>()
42            || !self.untyped_property.has_value_type::<T>()
43            || !pax_value::is_typed_binding_safe::<T>()
44        {
45            return None;
46        }
47        // Both adapter and storage types were checked. Replacement preserves T
48        // and the generational property handle; no borrowed value escapes.
49        let source = Property::<T>::new_from_untyped(self.untyped_property.clone());
50        Some(Property::computed_with_name(
51            move || source.get(),
52            &[self.untyped_property.clone()],
53            name,
54        ))
55    }
56
57    // Returns the underlying untyped property for dependency graph wiring.
58    pub fn get_untyped_property(&self) -> &UntypedProperty {
59        &self.untyped_property
60    }
61
62    /// Reads the current value as a `PaxValue`.
63    pub fn get_as_pax_value(&self) -> PaxValue {
64        self.converted_to_pax_value.get()
65    }
66
67    /// Reads the current `PaxValue` by reference.
68    pub fn read_pax_value_ref<V>(&self, f: impl FnOnce(&PaxValue) -> V) -> V {
69        self.converted_to_pax_value.read(f)
70    }
71}