use crate::{
error_views::{ErrorViews, ServerErrorData},
errors::ServerError,
path::*,
state::TemplateState,
stores::ImmutableStore,
};
use serde_json::Value;
use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc};
use sycamore::web::Html;
/// The status of a build-time render.
#[derive(Debug)]
pub(crate) enum RenderStatus {
/// The render is proceeding well.
Ok,
/// There was an error.
Err(ServerError),
/// The render was cancelled, since a widget couldn't be rendered at
/// build-time.
Cancelled,
}
impl Default for RenderStatus {
fn default() -> Self {
Self::Ok
}
}
/// The different modes of rendering on the engine-side. On the browser-side,
/// there is only one mode of rendering.
///
/// Ths render mode is primarily used to inform the non-delayed widgets of how
/// they should render.
#[cfg(engine)]
#[derive(Clone, Debug)]
pub(crate) enum RenderMode<G: Html> {
/// We're rendering at build-time. Any non-delayed widgets should render if
/// they are not going to alter the render properties of their caller.
/// Otherwise, they should silently fail the render and set the attached
/// [`Cell`] of this variant to `true` to inform the renderer.
Build {
/// Whether or not the render was cancelled due to a capsule being
/// unable to be rendered (having this determined *during* the
/// render avoids the need for the user to specify
/// all their pages' dependencies (which might be impossible with
/// incremental generation)).
render_status: Rc<RefCell<RenderStatus>>,
/// The render configuration for widgets. This will include both widgets
/// that are safe to be built at build-time, and widgets that
/// are not.
widget_render_cfg: HashMap<String, String>,
/// The app's immutable store. (This is cheap to clone.)
immutable_store: ImmutableStore,
/// An accumulator of the widget states involved in rendering this
/// template. We need to be able to collect these to later send
/// them to clients for hydration.
///
/// Importantly, widget states are *not* fallible at build-time!
/// Any errors will be propagated to terminate the build.
widget_states: Rc<RefCell<HashMap<PathMaybeWithLocale, (String, Value)>>>,
/// A list of widget paths that are either nonexistent, or able to be
/// incrementally generated. The build process should parse these and
/// try to build them all (failing entirely on any that actually don't
/// exist, or whose generation processes fail).
///
/// This system means widget paths that aren't in build paths
/// declarations can still be used without template rescheduling
/// being required, which is just logical (Perseus should be
/// able to just figure this one out).
possibly_incremental_paths: Rc<RefCell<Vec<PathWithoutLocale>>>,
},
/// We're rendering at request-time in order to determine what the
/// dependencies of this page/widget are. Each widget should check if
/// its state is available in the given map, proceeding with its
/// render if it is, or simply adding its route to a simple accumulator if
/// not.
///
/// Once we get to the last layer of dependencies, the accumulator will come
/// out with nothing new, and then the return value is the
/// fully-rendered content!
Request {
/// The widget states and attached capsule names. Each of these is
/// fallible, and the widget component will render an
/// appropriate error page if necessary.
#[allow(clippy::type_complexity)]
widget_states: Rc<HashMap<PathMaybeWithLocale, Result<TemplateState, ServerErrorData>>>,
/// The app's error views.
error_views: Arc<ErrorViews<G>>,
/// A list of the paths to widgets that haven't yet been resolved in any
/// way. These will be deduplicated and then resolved in
/// parallel, along with having their states built.
///
/// These paths do not contain the locale because a capsule from a
/// different locale can never be included.
unresolved_widget_accumulator: Rc<RefCell<Vec<PathWithoutLocale>>>,
},
// These may have the same outcome for widgets, but they could in future be used for
// additional mode-specific data
/// We're rendering a head, where widgets are not allowed.
Head,
/// We're rendering an error, where widgets are not allowed.
Error,
/// We're rendering headers, where widgets are not allowed.
Headers,
}