use std::any::type_name;
use std::borrow::Cow;
use std::fmt::{Debug, Formatter};
use std::marker::PhantomData;
use std::sync::Arc;
use crate::{AnySendObject, BaseView, DefiningMethod, ObjectRef, PhlowView, ViewInstance};
type ItemsComputation<T, Item> = dyn Fn(&T) -> Vec<Item> + Send + Sync + 'static;
type ItemTextComputation<Item> = dyn for<'a> Fn(&'a Item) -> Cow<'a, str> + Send + Sync + 'static;
type InstanceItemTextComputation = dyn for<'a> Fn(ObjectRef<'a>) -> String + Send + Sync + 'static;
type InstanceSendComputation =
dyn for<'a> Fn(ObjectRef<'a>) -> Option<AnySendObject> + Send + Sync + 'static;
type SendComputation<Item, SendItem> = dyn Fn(&Item) -> SendItem + Send + Sync + 'static;
#[repr(transparent)]
pub struct ProtoListView<T: ?Sized>(BaseView, PhantomData<fn() -> T>);
impl<T: ?Sized> ProtoListView<T> {
pub fn new(defining_method: Option<DefiningMethod>) -> Self {
Self(BaseView::new(defining_method), PhantomData)
}
pub fn title(mut self, title: impl Into<String>) -> Self {
self.0.title = title.into();
self
}
pub fn priority(mut self, priority: usize) -> Self {
self.0.priority = priority;
self
}
pub fn items<Item: Send>(
self,
items_block: impl Fn(&T) -> Vec<Item> + Send + Sync + 'static,
) -> ListViewWithoutSend<T, Item> {
ListViewWithoutSend::new(self, items_block)
}
}
#[allow(unused)]
pub struct ListViewWithoutSend<T: ?Sized, Item> {
proto_list: ProtoListView<T>,
items_computation: Arc<ItemsComputation<T, Item>>,
item_text_computation: Arc<ItemTextComputation<Item>>,
}
impl<T: ?Sized, Item> ListViewWithoutSend<T, Item> {
fn new(
proto_list: ProtoListView<T>,
items_computation: impl Fn(&T) -> Vec<Item> + Send + Sync + 'static,
) -> Self {
Self {
proto_list,
items_computation: Arc::new(items_computation),
item_text_computation: Arc::new(|_item| type_name::<Item>().into()),
}
}
fn compute_items_sync(&self, object: &T) -> Vec<Item> {
(self.items_computation)(object)
}
}
impl<T: 'static, Item: Send + 'static> ListViewWithoutSend<T, Item> {
fn create_list_instance(&self, object: ObjectRef<'_>) -> ListViewInstance {
let object = unsafe { object.cast::<T>() };
let items = self
.compute_items_sync(object)
.into_iter()
.map(|item| AnySendObject::new(item))
.collect();
let item_text_computation = self.item_text_computation.clone();
ListViewInstance {
base_view: self.proto_list.0.clone(),
items,
item_text_computation: Arc::new(move |item| {
(unsafe { item_text_computation(item.cast()) }).to_string()
}),
send_computation: Arc::new(move |_| None),
}
}
}
impl<T: ?Sized, Item: Send> ListViewWithoutSend<T, Item> {
pub fn item_text(
mut self,
item_text: impl Fn(&Item) -> String + Send + Sync + 'static,
) -> Self {
self.item_text_computation = Arc::new(move |item| item_text(item).into());
self
}
pub fn send<SendItem: Send>(
self,
item_send_block: impl Fn(&Item) -> SendItem + Send + Sync + 'static,
) -> ListViewWithSend<T, Item, SendItem> {
ListViewWithSend::new(self, item_send_block)
}
}
impl<T: ?Sized, Item: Send + Clone> ListViewWithoutSend<T, Item> {
pub fn send_clone(self) -> ListViewWithSend<T, Item, Item> {
self.send(|item| item.clone())
}
}
#[allow(unused)]
pub struct ListViewWithSend<T: ?Sized, Item, SendItem> {
proto_list: ListViewWithoutSend<T, Item>,
send_computation: Arc<SendComputation<Item, SendItem>>,
}
impl<T: ?Sized, Item, SendItem> ListViewWithSend<T, Item, SendItem> {
fn new(
proto_list: ListViewWithoutSend<T, Item>,
send_computation: impl Fn(&Item) -> SendItem + Send + Sync + 'static,
) -> Self {
Self {
proto_list,
send_computation: Arc::new(send_computation),
}
}
}
impl<T: 'static, Item: Send + 'static> PhlowView for ListViewWithoutSend<T, Item> {
fn get_title(&self) -> &str {
self.proto_list.0.title.as_str()
}
fn get_priority(&self) -> usize {
self.proto_list.0.priority
}
fn get_view_type(&self) -> &str {
Self::view_type()
}
fn get_defining_method(&self) -> Option<&DefiningMethod> {
self.proto_list.0.defining_method.as_ref()
}
fn create_instance(&self, object: ObjectRef<'_>) -> Box<dyn ViewInstance> {
Box::new(self.create_list_instance(object))
}
fn view_type() -> &'static str
where
Self: Sized,
{
"list_view"
}
}
impl<T: 'static, Item: Send + 'static, SendItem: Send + 'static> PhlowView
for ListViewWithSend<T, Item, SendItem>
{
fn get_title(&self) -> &str {
self.proto_list.proto_list.0.title.as_str()
}
fn get_priority(&self) -> usize {
self.proto_list.proto_list.0.priority
}
fn get_view_type(&self) -> &str {
Self::view_type()
}
fn get_defining_method(&self) -> Option<&DefiningMethod> {
self.proto_list.proto_list.0.defining_method.as_ref()
}
fn create_instance(&self, object: ObjectRef<'_>) -> Box<dyn ViewInstance> {
let send_computation = self.send_computation.clone();
let mut instance = self.proto_list.create_list_instance(object);
instance.send_computation = Arc::new(move |item| {
Some(AnySendObject::new(unsafe { send_computation(item.cast()) }))
});
Box::new(instance)
}
fn view_type() -> &'static str {
"list_view"
}
}
pub struct ListViewInstance {
base_view: BaseView,
items: Vec<AnySendObject>,
item_text_computation: Arc<InstanceItemTextComputation>,
send_computation: Arc<InstanceSendComputation>,
}
impl ViewInstance for ListViewInstance {
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 {
"list_view"
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl ListViewInstance {
pub fn items_count(&self) -> usize {
self.items.len()
}
pub fn get_item_text(&self, item_index: usize) -> Option<String> {
self.items.get(item_index).map(|item| {
ObjectRef::with_any(item.as_any(), |item| {
((self.item_text_computation)(item)).to_string()
})
})
}
pub fn get_item_to_send(&self, item_index: usize) -> Option<AnySendObject> {
self.items.get(item_index).and_then(|item| {
ObjectRef::with_any(item.as_any(), |item| (self.send_computation)(item))
})
}
}
impl<T: ?Sized, Item> Debug for ListViewWithoutSend<T, Item> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct(type_name::<Self>()).finish()
}
}
impl<T: ?Sized, Item, SendItem> Debug for ListViewWithSend<T, Item, SendItem> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct(type_name::<Self>()).finish()
}
}
impl<T: ?Sized> Clone for ProtoListView<T> {
fn clone(&self) -> Self {
Self(self.0.clone(), PhantomData)
}
}
impl<T: ?Sized, Item> Clone for ListViewWithoutSend<T, Item> {
fn clone(&self) -> Self {
Self {
proto_list: self.proto_list.clone(),
items_computation: self.items_computation.clone(),
item_text_computation: self.item_text_computation.clone(),
}
}
}
impl<T: ?Sized, Item, SendItem> Clone for ListViewWithSend<T, Item, SendItem> {
fn clone(&self) -> Self {
Self {
proto_list: self.proto_list.clone(),
send_computation: self.send_computation.clone(),
}
}
}