cocotte 0.1.0

A convenient way to make a Ratatui
Documentation
//! Sub-application traits and helpers.

use ratatui::Frame;
use ratatui::layout::{Constraint, Rect};

/// The bricks that will be assembled by `define_sub_apps`.
///
/// A [`SubApp`] receives events, can mutate shared application state and renders itself into the area assigned by the parent [`crate::App`].
pub trait SubApp<E, S> {
    /// Handles an input or application event.
    fn handle_input(&mut self, event: &mut E, state: &mut S);
    /// Renders the sub-app into the provided area.
    fn render(&self, frame: &mut Frame, area: Rect, state: &mut S);
    /// Returns the layout constraint used to size this sub-app vertically.
    fn constraints(&self) -> Constraint;
}

/// Defines a combined sub-app enum and a constructor function.
///
/// The generated `make_app()` function returns a fully constructed [`crate::App`].
///
/// # Example
///
/// ```no_run
/// # use cocotte::{define_sub_apps, SubApp};
/// # use cocotte::eyre::Result;
/// # use cocotte::ratatui::{Frame, layout::{Constraint, Rect}};
/// # struct Event;
/// # struct State;
/// # struct Pane;
/// # impl Pane { fn new() -> Self { Self } }
/// # impl SubApp<Event, State> for Pane {
/// #     fn handle_input(&mut self, _event: &mut Event, _state: &mut State) {}
/// #     fn render(&self, _frame: &mut Frame, _area: Rect, _state: &mut State) {}
/// #     fn constraints(&self) -> Constraint { Constraint::Fill(1) }
/// # }
/// define_sub_apps! {
///     event = Event;
///     state = State;
///     Pane(Pane) => Pane::new(),
/// }
///
/// # fn demo() -> Result<()> {
/// let _app = make_app()?;
/// # Ok(())
/// # }
/// ```
#[macro_export]
macro_rules! define_sub_apps {
    (
        event = $event_ty:path;
        state = $state_ty:path;
        $( $variant:ident ( $ty:path ) => $ctor:expr ),+ $(,)?
    ) => {
        pub enum SubAppView {
            $( $variant($ty), )+
        }

        impl $crate::sub_app::SubApp<$event_ty, $state_ty> for SubAppView {
            fn handle_input(&mut self, event: &mut $event_ty, state: &mut $state_ty) {
                match self {
                    $(
                        Self::$variant(x) => {
                            <$ty as $crate::sub_app::SubApp<$event_ty,$state_ty>>::handle_input(
                                x,
                                event,
                                state,
                            )
                        }
                    ),+
                }
            }

            fn render(
                &self,
                frame: &mut $crate::ratatui::Frame,
                area: $crate::ratatui::layout::Rect,
                state: &mut $state_ty,
            ) {
                match self {
                    $(
                        Self::$variant(x) => {
                            <$ty as $crate::sub_app::SubApp<$event_ty, $state_ty>>::render(
                                x,
                                frame,
                                area,
                                state,
                            )
                        }
                    ),+
                }
            }

            fn constraints(&self) -> $crate::ratatui::layout::Constraint {
                match self {
                    $(
                        Self::$variant(x) => {
                            <$ty as $crate::sub_app::SubApp<$event_ty, $state_ty>>::constraints(x)
                        }
                    ),+
                }
            }
        }

        pub fn make_app() -> $crate::eyre::Result<$crate::App<$event_ty, SubAppView, $state_ty>> {
            $crate::App::new(vec![ $( SubAppView::$variant($ctor), )+ ])
        }
    };
}