use alloc::boxed::Box;
use alloc::rc::Rc;
use alloc::sync::Arc;
use core::ops::Deref;
use crate::environment::Environment;
use crate::message::MessageResult;
use crate::{MessageContext, Mut, ViewElement};
pub trait ViewMarker {}
pub trait View<State, Action, Context: ViewPathTracker>: ViewMarker + 'static {
type Element: ViewElement;
type ViewState;
fn build(&self, ctx: &mut Context, app_state: &mut State) -> (Self::Element, Self::ViewState);
fn rebuild(
&self,
prev: &Self,
view_state: &mut Self::ViewState,
ctx: &mut Context,
element: Mut<'_, Self::Element>,
app_state: &mut State,
);
fn teardown(
&self,
view_state: &mut Self::ViewState,
ctx: &mut Context,
element: Mut<'_, Self::Element>,
);
fn message(
&self,
view_state: &mut Self::ViewState,
message: &mut MessageContext,
element: Mut<'_, Self::Element>,
app_state: &mut State,
) -> MessageResult<Action>;
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct ViewId(u64);
impl ViewId {
#[must_use]
pub const fn new(raw: u64) -> Self {
Self(raw)
}
#[must_use]
pub const fn routing_id(self) -> u64 {
self.0
}
}
pub trait ViewPathTracker {
fn environment(&mut self) -> &mut Environment;
fn push_id(&mut self, id: ViewId);
fn pop_id(&mut self);
fn view_path(&mut self) -> &[ViewId];
fn with_id<R>(&mut self, id: ViewId, f: impl FnOnce(&mut Self) -> R) -> R {
self.push_id(id);
let res = f(self);
self.pop_id();
res
}
}
impl<V: ?Sized> ViewMarker for Box<V> {}
impl<State, Action, Context, V> View<State, Action, Context> for Box<V>
where
Context: ViewPathTracker,
V: View<State, Action, Context> + ?Sized,
{
type Element = V::Element;
type ViewState = V::ViewState;
fn build(&self, ctx: &mut Context, app_state: &mut State) -> (Self::Element, Self::ViewState) {
self.deref().build(ctx, app_state)
}
fn rebuild(
&self,
prev: &Self,
view_state: &mut Self::ViewState,
ctx: &mut Context,
element: Mut<'_, Self::Element>,
app_state: &mut State,
) {
self.deref()
.rebuild(prev, view_state, ctx, element, app_state);
}
fn teardown(
&self,
view_state: &mut Self::ViewState,
ctx: &mut Context,
element: Mut<'_, Self::Element>,
) {
self.deref().teardown(view_state, ctx, element);
}
fn message(
&self,
view_state: &mut Self::ViewState,
message: &mut MessageContext,
element: Mut<'_, Self::Element>,
app_state: &mut State,
) -> MessageResult<Action> {
self.deref()
.message(view_state, message, element, app_state)
}
}
#[allow(unnameable_types)] #[derive(Debug)]
pub struct RcState<ViewState> {
view_state: ViewState,
dirty: bool,
}
impl<V: ?Sized> ViewMarker for Arc<V> {}
impl<State, Action, Context, V> View<State, Action, Context> for Arc<V>
where
Context: ViewPathTracker,
V: View<State, Action, Context> + ?Sized,
{
type Element = V::Element;
type ViewState = RcState<V::ViewState>;
fn build(&self, ctx: &mut Context, app_state: &mut State) -> (Self::Element, Self::ViewState) {
let (element, view_state) = self.deref().build(ctx, app_state);
(
element,
RcState {
view_state,
dirty: false,
},
)
}
fn rebuild(
&self,
prev: &Self,
view_state: &mut Self::ViewState,
ctx: &mut Context,
element: Mut<'_, Self::Element>,
app_state: &mut State,
) {
#![expect(clippy::use_self, reason = "`Arc::ptr_eq` is the canonical form")]
if core::mem::take(&mut view_state.dirty) || !Arc::ptr_eq(self, prev) {
self.deref()
.rebuild(prev, &mut view_state.view_state, ctx, element, app_state);
}
}
fn teardown(
&self,
view_state: &mut Self::ViewState,
ctx: &mut Context,
element: Mut<'_, Self::Element>,
) {
self.deref()
.teardown(&mut view_state.view_state, ctx, element);
}
fn message(
&self,
view_state: &mut Self::ViewState,
message: &mut MessageContext,
element: Mut<'_, Self::Element>,
app_state: &mut State,
) -> MessageResult<Action> {
let message_result =
self.deref()
.message(&mut view_state.view_state, message, element, app_state);
if matches!(message_result, MessageResult::RequestRebuild) {
view_state.dirty = true;
}
message_result
}
}
impl<V: ?Sized> ViewMarker for Rc<V> {}
impl<State, Action, Context, V> View<State, Action, Context> for Rc<V>
where
Context: ViewPathTracker,
V: View<State, Action, Context> + ?Sized,
{
type Element = V::Element;
type ViewState = RcState<V::ViewState>;
fn build(&self, ctx: &mut Context, app_state: &mut State) -> (Self::Element, Self::ViewState) {
let (element, view_state) = self.deref().build(ctx, app_state);
(
element,
RcState {
view_state,
dirty: false,
},
)
}
fn rebuild(
&self,
prev: &Self,
view_state: &mut Self::ViewState,
ctx: &mut Context,
element: Mut<'_, Self::Element>,
app_state: &mut State,
) {
#![expect(clippy::use_self, reason = "`Rc::ptr_eq` is the canonical form")]
if core::mem::take(&mut view_state.dirty) || !Rc::ptr_eq(self, prev) {
self.deref()
.rebuild(prev, &mut view_state.view_state, ctx, element, app_state);
}
}
fn teardown(
&self,
view_state: &mut Self::ViewState,
ctx: &mut Context,
element: Mut<'_, Self::Element>,
) {
self.deref()
.teardown(&mut view_state.view_state, ctx, element);
}
fn message(
&self,
view_state: &mut Self::ViewState,
message: &mut MessageContext,
element: Mut<'_, Self::Element>,
app_state: &mut State,
) -> MessageResult<Action> {
let message_result =
self.deref()
.message(&mut view_state.view_state, message, element, app_state);
if matches!(message_result, MessageResult::RequestRebuild) {
view_state.dirty = true;
}
message_result
}
}