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 NameComputation<T> = dyn for<'a> Fn(&'a T) -> Cow<'a, str> + Send + Sync + 'static;
type RowSendComputation<T> = dyn for<'a> Fn(&'a T) -> Option<AnySendObject> + Send + Sync + 'static;
type ItemTextComputation<Item> = dyn for<'a> Fn(&'a Item) -> Cow<'a, str> + Send + Sync + 'static;
type SentItemComputation<Item, SentItem> =
dyn for<'a> Fn(&'a Item) -> Option<SentItem> + Send + Sync + 'static;
type InstanceSendComputation =
dyn for<'a> Fn(ObjectRef<'a>) -> Option<AnySendObject> + Send + Sync + 'static;
#[repr(transparent)]
pub struct ProtoInfoView<T: ?Sized>(BaseView, PhantomData<fn() -> T>);
impl<T: ?Sized> ProtoInfoView<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 struct Row<T: ?Sized, Item> {
name_computation: Arc<NameComputation<T>>,
item_computation: ItemComputation<T, Item>,
send_computation: Option<Arc<RowSendComputation<T>>>,
text_computation: Arc<ItemTextComputation<Item>>,
}
pub struct InfoView<T: ?Sized, I, Item> {
previous_row: Arc<I>,
row: Row<T, Item>,
}
impl<Item, T: ?Sized, I: InfoRow<T>> InfoView<T, I, Item> {}
pub trait InfoRow<T: ?Sized>: Send + Sync {
fn row<Next, Builder>(
self,
builder: impl FnOnce(ProtoRowBuilder<T>) -> Builder,
) -> InfoView<T, Self, Next>
where
Self: Sized,
Builder: RowBuilder<T, Next>,
{
let builder = builder(ProtoRowBuilder {
phantom_data: Default::default(),
});
InfoView {
previous_row: Arc::new(self),
row: builder.into_row(),
}
}
fn proto_view(&self) -> &ProtoInfoView<T>;
fn compute_row_text<'a>(&self, object: &'a T) -> (Cow<'a, str>, String);
fn compute_item_to_send(&self, object: &T) -> Option<AnySendObject>;
fn accumulate_rows(&self, rows: &mut Vec<Arc<dyn InfoRow<T>>>);
}
pub trait RowBuilder<T: ?Sized, Item> {
fn into_row(self) -> Row<T, Item>;
}
pub struct ProtoRowBuilder<T: ?Sized> {
phantom_data: PhantomData<T>,
}
pub struct RowBuilderWithName<T: ?Sized> {
name_computation: Arc<NameComputation<T>>,
}
pub struct RowBuilderWithoutSend<T: ?Sized, Item: ?Sized> {
name_computation: Arc<NameComputation<T>>,
item_computation: ItemComputation<T, Item>,
text_computation: Arc<ItemTextComputation<Item>>,
}
pub struct RowBuilderWithSend<T: ?Sized, Item: ?Sized, SentItem: Send> {
name_computation: Arc<NameComputation<T>>,
item_computation: ItemComputation<T, Item>,
send_computation: Arc<SentItemComputation<Item, SentItem>>,
text_computation: Arc<ItemTextComputation<Item>>,
}
impl<T: ?Sized, Item> RowBuilder<T, Item> for RowBuilderWithoutSend<T, Item> {
fn into_row(self) -> Row<T, Item> {
Row {
name_computation: self.name_computation,
item_computation: self.item_computation,
send_computation: None,
text_computation: self.text_computation,
}
}
}
impl<T: 'static + ?Sized, Item: 'static, SentItem: Send + 'static> RowBuilder<T, Item>
for RowBuilderWithSend<T, Item, SentItem>
{
fn into_row(self) -> Row<T, Item> {
Row {
name_computation: self.name_computation,
item_computation: self.item_computation.clone(),
send_computation: Some(Arc::new(move |object| {
self.item_computation.value_do(object, |item| {
(self.send_computation)(item).map(AnySendObject::new)
})
})),
text_computation: self.text_computation,
}
}
}
impl<T: ?Sized> ProtoRowBuilder<T> {
pub fn named(self, name: impl Into<String>) -> RowBuilderWithName<T> {
let cow = Cow::from(name.into());
RowBuilderWithName {
name_computation: Arc::new(move |_item| cow.clone()),
}
}
pub fn named_str(self, name: &'static str) -> RowBuilderWithName<T> {
RowBuilderWithName {
name_computation: Arc::new(move |_item| Cow::from(name)),
}
}
pub fn name(self, f: impl Fn(&T) -> String + Send + Sync + 'static) -> RowBuilderWithName<T> {
RowBuilderWithName {
name_computation: Arc::new(move |item| f(item).into()),
}
}
pub fn name_str(self, f: impl Fn(&T) -> &str + Send + Sync + 'static) -> RowBuilderWithName<T> {
RowBuilderWithName {
name_computation: Arc::new(move |item| f(item).into()),
}
}
}
impl<T: ?Sized> RowBuilderWithName<T> {
pub fn item_ref<Next>(
self,
item: impl Fn(&T) -> &Next + Send + Sync + 'static,
) -> RowBuilderWithoutSend<T, Next> {
RowBuilderWithoutSend {
name_computation: self.name_computation,
item_computation: ItemComputation::Ref(Arc::new(item)),
text_computation: Arc::new(|_item| type_name::<Next>().into()),
}
}
pub fn item<Next>(
self,
item: impl Fn(&T) -> Next + Send + Sync + 'static,
) -> RowBuilderWithoutSend<T, Next> {
RowBuilderWithoutSend {
name_computation: self.name_computation,
item_computation: ItemComputation::Owned(Arc::new(item)),
text_computation: Arc::new(|_item| type_name::<Next>().into()),
}
}
}
impl<T: ?Sized, Item: ?Sized> RowBuilderWithoutSend<T, Item> {
pub fn text(mut self, text: impl Fn(&Item) -> String + Send + Sync + 'static) -> Self {
self.text_computation = Arc::new(move |item| text(item).into());
self
}
pub fn text_str(mut self, text: impl Fn(&Item) -> &str + Send + Sync + 'static) -> Self {
self.text_computation = Arc::new(move |item| text(item).into());
self
}
pub fn send<SentItem: Send + Sync>(
self,
send: impl Fn(&Item) -> SentItem + Send + Sync + 'static,
) -> RowBuilderWithSend<T, Item, SentItem> {
RowBuilderWithSend {
name_computation: self.name_computation,
item_computation: self.item_computation,
send_computation: Arc::new(move |item| Some(send(item))),
text_computation: self.text_computation,
}
}
}
impl<T: ?Sized, Item: Copy + Send + Sync> RowBuilderWithoutSend<T, Item> {
pub fn send_copy(self) -> RowBuilderWithSend<T, Item, Item> {
self.send(|item| *item)
}
}
impl<T: ?Sized, Item: Clone + Send + Sync> RowBuilderWithoutSend<T, Item> {
pub fn send_clone(self) -> RowBuilderWithSend<T, Item, Item> {
self.send(|item| item.clone())
}
}
impl<T: ?Sized, Item: Send + Sync> RowBuilderWithoutSend<T, Option<Item>> {
pub fn try_send<SentItem: Send + Sync>(
self,
send: impl Fn(&Item) -> SentItem + Send + Sync + 'static,
) -> RowBuilderWithSend<T, Option<Item>, SentItem> {
RowBuilderWithSend {
name_computation: self.name_computation,
item_computation: self.item_computation,
send_computation: Arc::new(move |item| item.as_ref().map(&send)),
text_computation: self.text_computation,
}
}
}
impl<T: ?Sized, Item: Copy + Send + Sync> RowBuilderWithoutSend<T, Option<Item>> {
pub fn try_send_copy(self) -> RowBuilderWithSend<T, Option<Item>, Item> {
self.try_send(|item| *item)
}
}
impl<T: ?Sized, Item: Clone + Send + Sync> RowBuilderWithoutSend<T, Option<Item>> {
pub fn try_send_clone(self) -> RowBuilderWithSend<T, Option<Item>, Item> {
self.try_send(|item| item.clone())
}
}
impl<T: ?Sized, Item: ?Sized, SentItem: Send> RowBuilderWithSend<T, Item, SentItem> {
pub fn text(mut self, text: impl Fn(&Item) -> String + Send + Sync + 'static) -> Self {
self.text_computation = Arc::new(move |item| text(item).into());
self
}
pub fn text_str(mut self, text: impl Fn(&Item) -> &str + Send + Sync + 'static) -> Self {
self.text_computation = Arc::new(move |item| text(item).into());
self
}
}
impl<Item: 'static, T: ?Sized + 'static, I: InfoRow<T> + 'static> InfoRow<T>
for InfoView<T, I, Item>
{
fn proto_view(&self) -> &ProtoInfoView<T> {
self.previous_row.proto_view()
}
fn compute_row_text<'a>(&self, object: &'a T) -> (Cow<'a, str>, String) {
let row_name = (self.row.name_computation)(object);
let row_str = self.row.item_computation.value_do(object, |row_item| {
(self.row.text_computation)(row_item).to_string()
});
(row_name, row_str)
}
fn compute_item_to_send(&self, object: &T) -> Option<AnySendObject> {
self.row
.send_computation
.as_ref()
.and_then(|send_computation| send_computation(object))
}
fn accumulate_rows(&self, rows: &mut Vec<Arc<dyn InfoRow<T>>>) {
self.previous_row.accumulate_rows(rows);
rows.push(Arc::new(self.clone()));
}
}
impl<T: ?Sized> InfoRow<T> for ProtoInfoView<T> {
fn proto_view(&self) -> &ProtoInfoView<T> {
self
}
fn compute_row_text<'a>(&self, _object: &'a T) -> (Cow<'a, str>, String) {
("".into(), "".to_string())
}
fn compute_item_to_send(&self, _object: &T) -> Option<AnySendObject> {
None
}
fn accumulate_rows(&self, _rows: &mut Vec<Arc<dyn InfoRow<T>>>) {}
}
impl<Item, T: ?Sized, I: InfoRow<T>> Debug for InfoView<T, I, Item> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct(type_name::<Self>()).finish()
}
}
impl<T: ?Sized> Clone for ProtoInfoView<T> {
fn clone(&self) -> Self {
Self(self.0.clone(), PhantomData)
}
}
impl<T: ?Sized, Item> Clone for Row<T, Item> {
fn clone(&self) -> Self {
Self {
name_computation: self.name_computation.clone(),
item_computation: self.item_computation.clone(),
send_computation: self.send_computation.clone(),
text_computation: self.text_computation.clone(),
}
}
}
impl<Item, T: ?Sized, I: InfoRow<T>> Clone for InfoView<T, I, Item> {
fn clone(&self) -> Self {
Self {
previous_row: self.previous_row.clone(),
row: self.row.clone(),
}
}
}
enum ItemComputation<T: ?Sized, Item: ?Sized> {
Owned(Arc<dyn Fn(&T) -> Item + Send + Sync>),
Ref(Arc<dyn for<'a> Fn(&'a T) -> &'a Item + Send + Sync>),
}
impl<T: ?Sized, Item> ItemComputation<T, Item> {
fn value_do<R>(&self, value: &T, block: impl Fn(&Item) -> R) -> R {
match self {
ItemComputation::Owned(f) => block(&f(value)),
ItemComputation::Ref(f) => block(f(value)),
}
}
}
impl<T: ?Sized, Item: ?Sized> Clone for ItemComputation<T, Item> {
fn clone(&self) -> Self {
match self {
ItemComputation::Owned(f) => Self::Owned(f.clone()),
ItemComputation::Ref(f) => Self::Ref(f.clone()),
}
}
}
impl<Item: 'static, T: 'static, I: InfoRow<T> + 'static> PhlowView for InfoView<T, I, Item> {
fn get_title(&self) -> &str {
self.proto_view().0.title.as_str()
}
fn get_priority(&self) -> usize {
self.proto_view().0.priority
}
fn get_view_type(&self) -> &str {
Self::view_type()
}
fn get_defining_method(&self) -> Option<&DefiningMethod> {
self.proto_view().0.defining_method.as_ref()
}
fn create_instance(&self, object: ObjectRef<'_>) -> Box<dyn ViewInstance> {
let object = unsafe { object.cast::<T>() };
let mut row_views = Vec::new();
self.accumulate_rows(&mut row_views);
let rows = row_views
.into_iter()
.map(|row| {
let (name, text) = row.compute_row_text(object);
InfoRowInstance {
name: name.into_owned(),
text,
send_computation: Arc::new(move |object| {
row.compute_item_to_send(unsafe { object.cast() })
}),
}
})
.collect();
Box::new(InfoViewInstance {
base_view: self.proto_view().0.clone(),
rows,
})
}
fn view_type() -> &'static str
where
Self: Sized,
{
"info_view"
}
}
pub struct InfoRowInstance {
pub name: String,
pub text: String,
pub send_computation: Arc<InstanceSendComputation>,
}
pub struct InfoViewInstance {
pub base_view: BaseView,
pub rows: Vec<InfoRowInstance>,
}
impl ViewInstance for InfoViewInstance {
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 {
"info_view"
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}