phlow 3.0.0

An engine for scripting reactive browsers in Rust by adding custom views to structures
Documentation
use std::any::type_name;
use std::borrow::Cow;
use std::fmt::{Debug, Formatter};
use std::marker::PhantomData;
use std::sync::Arc;

use crate::{BaseView, DefiningMethod, ObjectRef, PhlowView, ViewInstance};

type TextComputation<T> = dyn for<'a> Fn(&'a T) -> Cow<'a, str> + Send + Sync + 'static;

#[allow(unused)]
pub struct PhlowTextView<T: ?Sized> {
    base_view: BaseView,
    text_computation: Arc<TextComputation<T>>,
    phantom_data: PhantomData<fn() -> T>,
}

impl<T: ?Sized> PhlowTextView<T> {
    pub fn new(defining_method: Option<DefiningMethod>) -> Self {
        Self {
            base_view: BaseView::new(defining_method),
            text_computation: Arc::new(|_value| type_name::<T>().into()),
            phantom_data: PhantomData,
        }
    }

    pub fn title(mut self, title: impl Into<String>) -> Self {
        self.base_view.title = title.into();
        self
    }

    pub fn priority(mut self, priority: usize) -> Self {
        self.base_view.priority = priority;
        self
    }

    pub fn text(mut self, text_block: impl Fn(&T) -> String + Send + Sync + 'static) -> Self {
        self.text_computation = Arc::new(move |value| Cow::from(text_block(value)));
        self
    }

    pub fn text_ref(mut self, text_block: impl Fn(&T) -> &str + Send + Sync + 'static) -> Self {
        self.text_computation = Arc::new(move |value| Cow::from(text_block(value)));
        self
    }

    pub fn compute_text<'a>(&self, object: &'a T) -> Cow<'a, str> {
        (self.text_computation)(object)
    }
}

#[derive(Debug, Clone)]
pub struct TextViewInstance {
    pub base_view: BaseView,
    pub text: String,
}

impl ViewInstance for TextViewInstance {
    fn get_title(&self) -> &str {
        self.base_view.title.as_str()
    }

    fn get_priority(&self) -> usize {
        self.base_view.priority
    }

    fn get_view_type(&self) -> &str {
        "text_view"
    }

    fn as_any(&self) -> &dyn std::any::Any {
        self
    }
}

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

impl<T: 'static> PhlowView for PhlowTextView<T> {
    fn get_title(&self) -> &str {
        self.base_view.title.as_str()
    }

    fn get_priority(&self) -> usize {
        self.base_view.priority
    }

    fn get_view_type(&self) -> &str {
        Self::view_type()
    }

    fn get_defining_method(&self) -> Option<&DefiningMethod> {
        self.base_view.defining_method.as_ref()
    }

    fn create_instance(&self, object: ObjectRef<'_>) -> Box<dyn ViewInstance> {
        // SAFETY: the view is only instantiated with the receiver type it was defined for.
        let object = unsafe { object.cast::<T>() };
        Box::new(TextViewInstance {
            base_view: self.base_view.clone(),
            text: self.compute_text(object).into_owned(),
        })
    }

    fn view_type() -> &'static str
    where
        Self: Sized,
    {
        "text_view"
    }
}