use core::any::Any;
use std::rc::Rc;
use crate::ViewExt;
use nami::{Computed, SignalExt, signal::IntoComputed};
use waterui_core::{AnyView, Dynamic, Environment, View, handler::ViewBuilder};
#[derive(Debug)]
pub struct When<Condition, Then> {
condition: Condition,
then: Then,
}
impl<Condition, Then> When<Condition, Then>
where
Condition: IntoComputed<bool>,
{
pub const fn new(condition: Condition, then: Then) -> Self
where
Then: ViewBuilder,
{
Self { condition, then }
}
}
pub const fn when<Condition, Then>(condition: Condition, then: Then) -> When<Condition, Then>
where
Condition: IntoComputed<bool>,
Then: ViewBuilder,
{
When::new(condition, then)
}
impl<Condition, Then> View for When<Condition, Then>
where
Condition: IntoComputed<bool> + Clone,
Then: ViewBuilder,
{
fn body(self, _env: &Environment) -> impl View {
self.otherwise(|| {})
}
}
impl<Condition, Then> When<Condition, Then>
where
Condition: IntoComputed<bool>,
Then: ViewBuilder,
{
pub const fn or<C, V>(self, condition: C, then: V) -> WhenChain<Self, C, V>
where
C: IntoComputed<bool>,
V: ViewBuilder,
{
WhenChain {
prev: self,
condition,
then,
}
}
pub const fn otherwise<V>(self, otherwise: V) -> WhenComplete<Self, V>
where
V: ViewBuilder,
{
WhenComplete {
chain: self,
otherwise,
}
}
}
pub struct WhenChain<Prev, Condition, Then> {
prev: Prev,
condition: Condition,
then: Then,
}
impl<Prev, Condition, Then> core::fmt::Debug for WhenChain<Prev, Condition, Then> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("WhenChain").finish_non_exhaustive()
}
}
impl<Prev, Condition, Then> WhenChain<Prev, Condition, Then>
where
Condition: IntoComputed<bool>,
Then: ViewBuilder,
{
pub const fn or<C, V>(self, condition: C, then: V) -> WhenChain<Self, C, V>
where
C: IntoComputed<bool>,
V: ViewBuilder,
{
WhenChain {
prev: self,
condition,
then,
}
}
pub const fn otherwise<V>(self, otherwise: V) -> WhenComplete<Self, V>
where
V: ViewBuilder,
{
WhenComplete {
chain: self,
otherwise,
}
}
}
pub struct WhenComplete<Chain, Otherwise> {
chain: Chain,
otherwise: Otherwise,
}
impl<Chain, Otherwise> core::fmt::Debug for WhenComplete<Chain, Otherwise> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("WhenComplete").finish_non_exhaustive()
}
}
trait EvalChain: 'static {
fn all_static(&self) -> bool;
fn eval_static(&self) -> Option<AnyView>;
fn make_combined(&self) -> Computed<Option<usize>>;
fn build_branch(&self, index: usize) -> AnyView;
}
impl<C, T> EvalChain for When<C, T>
where
C: IntoComputed<bool> + Clone,
T: ViewBuilder,
{
fn all_static(&self) -> bool {
let signal = self.condition.clone().into_signal();
let any: &dyn Any = &signal;
any.downcast_ref::<bool>().is_some()
}
fn eval_static(&self) -> Option<AnyView> {
let signal = self.condition.clone().into_signal();
let any: &dyn Any = &signal;
if let Some(&cond) = any.downcast_ref::<bool>()
&& cond
{
return Some(self.then.build().anyview());
}
None
}
fn make_combined(&self) -> Computed<Option<usize>> {
let cond = self.condition.clone().into_computed();
cond.map(|v| if v { Some(0) } else { None }).computed()
}
fn build_branch(&self, index: usize) -> AnyView {
debug_assert_eq!(index, 0);
self.then.build().anyview()
}
}
impl<Prev, C, T> EvalChain for WhenChain<Prev, C, T>
where
Prev: EvalChain + BranchCount,
C: IntoComputed<bool> + Clone,
T: ViewBuilder,
{
fn all_static(&self) -> bool {
if !self.prev.all_static() {
return false;
}
let signal = self.condition.clone().into_signal();
let any: &dyn Any = &signal;
any.downcast_ref::<bool>().is_some()
}
fn eval_static(&self) -> Option<AnyView> {
if let Some(view) = self.prev.eval_static() {
return Some(view);
}
let signal = self.condition.clone().into_signal();
let any: &dyn Any = &signal;
if let Some(&cond) = any.downcast_ref::<bool>()
&& cond
{
return Some(self.then.build().anyview());
}
None
}
fn make_combined(&self) -> Computed<Option<usize>> {
let prev_combined = self.prev.make_combined();
let this_cond = self.condition.clone().into_computed();
let this_index = self.prev.branch_count();
prev_combined
.zip(&this_cond)
.map(move |(prev, cond): (Option<usize>, bool)| {
if prev.is_some() {
return prev;
}
if cond {
return Some(this_index);
}
None
})
.computed()
}
fn build_branch(&self, index: usize) -> AnyView {
let prev_len = self.prev.branch_count();
if index < prev_len {
self.prev.build_branch(index)
} else {
debug_assert_eq!(index, prev_len);
self.then.build().anyview()
}
}
}
trait BranchCount {
fn branch_count(&self) -> usize;
}
impl<C, T> BranchCount for When<C, T> {
fn branch_count(&self) -> usize {
1
}
}
impl<Prev: BranchCount, C, T> BranchCount for WhenChain<Prev, C, T> {
fn branch_count(&self) -> usize {
self.prev.branch_count() + 1
}
}
impl<Chain, Otherwise> View for WhenComplete<Chain, Otherwise>
where
Chain: EvalChain,
Otherwise: ViewBuilder,
{
fn body(self, _env: &Environment) -> impl View {
let Self { chain, otherwise } = self;
if chain.all_static() {
if let Some(view) = chain.eval_static() {
return view;
}
return otherwise.build().anyview();
}
let chain = Rc::new(chain);
let otherwise = Rc::new(otherwise);
Dynamic::watch(chain.make_combined(), move |index| {
index.map_or_else(|| otherwise.build().anyview(), |i| chain.build_branch(i))
})
.anyview()
}
}