use std::any::Any;
use std::ffi::c_void;
use std::fmt::{Debug, Display, Formatter};
use std::marker::PhantomData;
use std::ptr::NonNull;
use crate::{DefiningMethod, PhlowTextView, ProtoInfoView, ProtoListView};
#[derive(Clone, Copy)]
pub enum ObjectRef<'a> {
Erased(ErasedObjectRef<'a>),
Any(&'a dyn Any),
}
impl<'a> ObjectRef<'a> {
pub fn new<T>(value: &'a T) -> Self {
Self::Erased(ErasedObjectRef::new(value))
}
pub unsafe fn with_erased_ptr<R>(
ptr: NonNull<c_void>,
f: impl for<'b> FnOnce(ObjectRef<'b>) -> R,
) -> R {
f(ObjectRef::Erased(ErasedObjectRef {
ptr: ptr.as_ptr().cast(),
phantom_data: PhantomData,
}))
}
pub fn with_any<R>(any: &dyn Any, f: impl for<'b> FnOnce(ObjectRef<'b>) -> R) -> R {
f(ObjectRef::Any(any))
}
pub unsafe fn cast<T: 'static>(self) -> &'a T {
match self {
Self::Erased(obj) => unsafe { obj.cast() },
Self::Any(any) => any.downcast_ref().unwrap(),
}
}
}
#[derive(Clone, Copy)]
pub struct ErasedObjectRef<'a> {
ptr: *const (),
phantom_data: PhantomData<&'a ()>,
}
impl<'a> ErasedObjectRef<'a> {
pub fn new<T>(value: &'a T) -> Self {
Self {
ptr: value as *const T as *const (),
phantom_data: PhantomData,
}
}
pub unsafe fn with_erased_ptr<R>(
ptr: NonNull<c_void>,
f: impl for<'b> FnOnce(ErasedObjectRef<'b>) -> R,
) -> R {
f(ErasedObjectRef {
ptr: ptr.as_ptr().cast(),
phantom_data: PhantomData,
})
}
pub unsafe fn cast<T>(&self) -> &'a T {
unsafe { &*(self.ptr as *const T) }
}
}
pub trait ViewInstance: Send {
fn get_title(&self) -> &str;
fn get_priority(&self) -> usize;
fn get_view_type(&self) -> &str;
fn as_any(&self) -> &dyn Any;
}
pub trait PhlowView: Debug + Send + Sync {
fn get_title(&self) -> &str;
fn get_priority(&self) -> usize;
fn get_view_type(&self) -> &str;
fn get_defining_method(&self) -> Option<&DefiningMethod>;
fn create_instance(&self, object: ObjectRef<'_>) -> Box<dyn ViewInstance>;
fn view_type() -> &'static str
where
Self: Sized;
}
pub trait ProtoView<T: ?Sized>: PhlowView {
fn list(&self) -> ProtoListView<T> {
ProtoListView::new(self.get_defining_method().cloned())
}
fn info(&self) -> ProtoInfoView<T> {
ProtoInfoView::new(self.get_defining_method().cloned())
}
fn text(&self) -> PhlowTextView<T> {
PhlowTextView::new(self.get_defining_method().cloned())
}
}
pub struct PhlowProtoView<T: ?Sized> {
defining_method: Option<DefiningMethod>,
phantom_data: PhantomData<fn() -> T>,
}
impl<T: 'static + ?Sized> PhlowProtoView<T> {
pub fn new() -> Self {
Self {
defining_method: None,
phantom_data: PhantomData,
}
}
pub fn compiled(defining_method: DefiningMethod) -> Self {
Self {
defining_method: Some(defining_method),
phantom_data: PhantomData,
}
}
}
impl<T: 'static + ?Sized> Default for PhlowProtoView<T> {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct BaseView {
pub defining_method: Option<DefiningMethod>,
pub title: String,
pub priority: usize,
}
impl BaseView {
pub fn new(defining_method: Option<DefiningMethod>) -> Self {
Self {
defining_method,
title: "".to_string(),
priority: 10,
}
}
}
impl Clone for BaseView {
fn clone(&self) -> Self {
Self {
defining_method: self.defining_method.clone(),
title: self.title.clone(),
priority: self.priority,
}
}
}
impl<T: ?Sized> Display for PhlowProtoView<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "ProtoView")
}
}
impl<T: ?Sized> Debug for PhlowProtoView<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "ProtoView")
}
}
#[derive(Debug)]
struct ProtoViewInstance {
title: String,
priority: usize,
view_type: &'static str,
}
impl ViewInstance for ProtoViewInstance {
fn get_title(&self) -> &str {
self.title.as_str()
}
fn get_priority(&self) -> usize {
self.priority
}
fn get_view_type(&self) -> &str {
self.view_type
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl<T: ?Sized> PhlowView for PhlowProtoView<T> {
fn get_title(&self) -> &str {
"Untitled"
}
fn get_priority(&self) -> usize {
10
}
fn get_view_type(&self) -> &str {
Self::view_type()
}
fn get_defining_method(&self) -> Option<&DefiningMethod> {
self.defining_method.as_ref()
}
fn create_instance(&self, _object: ObjectRef<'_>) -> Box<dyn ViewInstance> {
Box::new(ProtoViewInstance {
title: self.get_title().to_string(),
priority: self.get_priority(),
view_type: Self::view_type(),
})
}
fn view_type() -> &'static str {
"proto_view"
}
}
impl<T: ?Sized> ProtoView<T> for PhlowProtoView<T> {}
impl<V: PhlowView + 'static> From<Box<V>> for Box<dyn PhlowView> {
fn from(value: Box<V>) -> Self {
value
}
}
pub trait IntoView {
fn into_view(self) -> Box<dyn PhlowView>;
}
impl<V: PhlowView + 'static> IntoView for V {
fn into_view(self) -> Box<dyn PhlowView> {
Box::new(self)
}
}