phlow 3.0.0

An engine for scripting reactive browsers in Rust by adding custom views to structures
Documentation
use crate::{DefiningMethod, ObjectRef, PhlowView};
use annotate::Function;
use std::any::{Any, type_name};
use std::fmt::{Debug, Formatter};
use std::ops::Deref;
use std::sync::Arc;

#[repr(transparent)]
pub struct PhlowObject<T: ?Sized = dyn Any>(Arc<T>);

impl<T: 'static> PhlowObject<T> {
    pub fn as_any(&self) -> AnyObject {
        AnyObject(self.0.clone())
    }
}

impl PhlowObject {
    pub fn from_any(object: AnyObject) -> Self {
        Self(object.0)
    }
}

impl<T> From<T> for PhlowObject<T> {
    fn from(value: T) -> Self {
        Self(Arc::new(value))
    }
}

impl<T: ?Sized> From<Arc<T>> for PhlowObject<T> {
    fn from(value: Arc<T>) -> Self {
        Self(value)
    }
}

impl<T: ?Sized> AsRef<T> for PhlowObject<T> {
    fn as_ref(&self) -> &T {
        self.0.as_ref()
    }
}

impl<T: ?Sized> Clone for PhlowObject<T> {
    fn clone(&self) -> Self {
        Self(self.0.clone())
    }
}

impl<T: ?Sized> Deref for PhlowObject<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<T: ?Sized> Debug for PhlowObject<T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_str("PhlowObject")
    }
}

#[repr(transparent)]
#[derive(Clone)]
pub struct AnyObject(Arc<dyn Any>);

impl AnyObject {
    pub fn new<T: Any>(object: T) -> Self {
        Self(Arc::new(object))
    }

    pub fn downcast<T: 'static>(self) -> PhlowObject<T> {
        if self.0.is::<T>() {
            let raw = Arc::into_raw(self.0) as *const T;
            unsafe { Arc::from_raw(raw).into() }
        } else {
            panic!("Failed to downcast AnyObject to {}", type_name::<T>());
        }
    }
}

impl From<Arc<dyn Any>> for AnyObject {
    fn from(value: Arc<dyn Any>) -> Self {
        Self(value)
    }
}

impl From<Box<dyn Any>> for AnyObject {
    fn from(value: Box<dyn Any>) -> Self {
        Self(Arc::from(value))
    }
}

impl From<AnyObject> for PhlowObject {
    fn from(value: AnyObject) -> Self {
        Self::from_any(value)
    }
}

impl Deref for AnyObject {
    type Target = dyn Any;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl Debug for AnyObject {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_str("AnyObject")
    }
}

#[repr(transparent)]
pub struct AnySendObject(Box<dyn Any + Send>);

impl AnySendObject {
    pub fn new<T: Send + 'static>(object: T) -> Self {
        Self(Box::new(object))
    }

    pub fn downcast_ref<T: 'static>(&self) -> &T {
        self.0
            .downcast_ref()
            .unwrap_or_else(|| panic!("Failed to downcast AnySendObject to {}", type_name::<T>()))
    }

    pub fn as_any(&self) -> &dyn Any {
        self.0.as_ref()
    }
}

pub struct PhlowVTable {
    pub type_name_fn: Option<Function>,
    pub to_string_fn: Option<Function>,
    pub as_view_fn: Option<Function>,
    pub defining_methods_fn: Option<Function>,
}

impl PhlowVTable {
    pub fn supports_type_name(&self) -> bool {
        self.type_name_fn.is_some()
    }

    pub fn type_name(&self) -> Option<&'static str> {
        self.type_name_fn
            .as_ref()
            .map(|f| f.call::<fn() -> &'static str, _>(|f| f()))
    }

    pub fn supports_to_string(&self) -> bool {
        self.to_string_fn.is_some()
    }

    pub fn to_string(&self, object: ObjectRef<'_>) -> Option<String> {
        self.to_string_fn
            .as_ref()
            .map(|f| f.call::<fn(ObjectRef<'_>) -> String, _>(|f| f(object)))
    }

    pub fn view_defining_methods_for_type(&self) -> Vec<DefiningMethod> {
        self.defining_methods_fn
            .as_ref()
            .map(|f| f.call::<fn() -> Vec<DefiningMethod>, _>(|f| f()))
            .unwrap_or_default()
    }

    pub fn defining_method_as_view(
        &self,
        method: &DefiningMethod,
        object: ObjectRef<'_>,
    ) -> Option<Box<dyn PhlowView>> {
        self.as_view_fn.as_ref().map(|f| {
            f.call::<fn(&DefiningMethod, ObjectRef<'_>) -> Box<dyn PhlowView>, _>(|f| {
                f(method, object)
            })
        })
    }

    pub fn views(&self, object: ObjectRef<'_>) -> Vec<Box<dyn PhlowView>> {
        self.view_defining_methods_for_type()
            .iter()
            .filter_map(move |method| self.defining_method_as_view(method, object))
            .collect()
    }
}