use super::rx_state::AnyFreeze;
use super::TemplateState;
use super::{MakeRx, MakeUnrx};
#[cfg(engine)] use crate::errors::*;
use crate::errors::{ClientError, ClientInvariantError};
#[cfg(engine)]
use crate::make_async_trait;
#[cfg(engine)]
use crate::template::{BlamedGeneratorResult, GeneratorResult};
#[cfg(engine)]
use crate::utils::AsyncFnReturn;
#[cfg(engine)]
use crate::Request;
#[cfg(engine)]
use futures::Future;
#[cfg(engine)]
use serde::de::DeserializeOwned;
#[cfg(any(client, doc))]
use serde::Deserialize;
use serde::Serialize;
use std::cell::RefCell;
use std::rc::Rc;
#[cfg(engine)]
make_async_trait!(
GlobalStateBuildFnType,
Result<TemplateState, ServerError>
);
#[cfg(engine)]
make_async_trait!(
GlobalStateRequestFnType,
Result<TemplateState, ServerError>,
req: Request
);
#[cfg(engine)]
make_async_trait!(
GlobalStateAmalgamationFnType,
Result<TemplateState, ServerError>,
build_state: TemplateState,
request_state: TemplateState
);
#[cfg(engine)]
make_async_trait!(
GlobalStateBuildUserFnType< S: Serialize + DeserializeOwned + MakeRx, V: Into< GeneratorResult<S> > >,
V
);
#[cfg(engine)]
make_async_trait!(
GlobalStateRequestUserFnType< S: Serialize + DeserializeOwned + MakeRx, V: Into< BlamedGeneratorResult<S> > >,
V,
req: Request
);
#[cfg(engine)]
make_async_trait!(
GlobalStateAmalgamationUserFnType< S: Serialize + DeserializeOwned + MakeRx, V: Into< BlamedGeneratorResult<S> > >,
V,
build_state: S,
request_state: S
);
#[cfg(engine)]
pub type GlobalStateBuildFn = Box<dyn GlobalStateBuildFnType + Send + Sync>;
#[cfg(engine)]
pub type GlobalStateRequestFn = Box<dyn GlobalStateRequestFnType + Send + Sync>;
#[cfg(engine)]
pub type GlobalStateAmalgamationFn = Box<dyn GlobalStateAmalgamationFnType + Send + Sync>;
#[derive(Default)]
pub struct GlobalStateCreator {
#[cfg(engine)]
build: Option<GlobalStateBuildFn>,
#[cfg(engine)]
request: Option<GlobalStateRequestFn>,
#[cfg(engine)]
amalgamation: Option<GlobalStateAmalgamationFn>,
}
impl std::fmt::Debug for GlobalStateCreator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GlobalStateCreator").finish()
}
}
impl GlobalStateCreator {
pub fn new() -> Self {
Self::default()
}
#[cfg(engine)]
pub fn build_state_fn<S, V>(
mut self,
val: impl GlobalStateBuildUserFnType<S, V> + Clone + Send + Sync + 'static,
) -> Self
where
S: Serialize + DeserializeOwned + MakeRx,
V: Into<GeneratorResult<S>>,
{
self.build = Some(Box::new(move || {
let val = val.clone();
async move {
let user_state = val
.call()
.await
.into()
.into_server_result("global_build_state", "GLOBAL_STATE".to_string())?;
let template_state: TemplateState = user_state.into();
Ok(template_state)
}
}));
self
}
#[cfg(any(client, doc))]
pub fn build_state_fn(self, _val: impl Fn() + 'static) -> Self {
self
}
#[cfg(engine)]
pub fn request_state_fn<S, V>(
mut self,
val: impl GlobalStateRequestUserFnType<S, V> + Clone + Send + Sync + 'static,
) -> Self
where
S: Serialize + DeserializeOwned + MakeRx,
V: Into<BlamedGeneratorResult<S>>,
{
self.request = Some(Box::new(move |req| {
let val = val.clone();
async move {
let user_state = val
.call(req)
.await
.into()
.into_server_result("global_request_state", "GLOBAL_STATE".to_string())?;
let template_state: TemplateState = user_state.into();
Ok(template_state)
}
}));
self
}
#[cfg(any(client, doc))]
pub fn request_state_fn(self, _val: impl Fn() + 'static) -> Self {
self
}
#[cfg(engine)]
pub fn amalgamate_states_fn<S, V>(
mut self,
val: impl GlobalStateAmalgamationUserFnType<S, V> + Clone + Send + Sync + 'static,
) -> Self
where
S: Serialize + DeserializeOwned + MakeRx + Send + Sync + 'static,
V: Into<BlamedGeneratorResult<S>>,
{
self.amalgamation = Some(Box::new(
move |build_state: TemplateState, request_state: TemplateState| {
let val = val.clone();
async move {
let typed_build_state = build_state.change_type::<S>();
let user_build_state = match typed_build_state.into_concrete() {
Ok(state) => state,
Err(err) => panic!(
"unrecoverable error in state amalgamation parameter derivation: {:#?}",
err
),
};
let typed_request_state = request_state.change_type::<S>();
let user_request_state = match typed_request_state.into_concrete() {
Ok(state) => state,
Err(err) => panic!(
"unrecoverable error in state amalgamation parameter derivation: {:#?}",
err
),
};
let user_state = val
.call(user_build_state, user_request_state)
.await
.into()
.into_server_result(
"global_amalgamate_states",
"GLOBAL_STATE".to_string(),
)?;
let template_state: TemplateState = user_state.into();
Ok(template_state)
}
},
));
self
}
#[cfg(any(client, doc))]
pub fn amalgamate_states_fn(self, _val: impl Fn() + 'static) -> Self {
self
}
#[cfg(engine)]
pub async fn get_build_state(&self) -> Result<TemplateState, ServerError> {
if let Some(get_build_state) = &self.build {
get_build_state.call().await
} else {
Err(BuildError::TemplateFeatureNotEnabled {
template_name: "GLOBAL_STATE".to_string(),
feature_name: "build_state".to_string(),
}
.into())
}
}
#[cfg(engine)]
pub async fn get_request_state(&self, req: Request) -> Result<TemplateState, ServerError> {
if let Some(get_request_state) = &self.request {
get_request_state.call(req).await
} else {
Err(BuildError::TemplateFeatureNotEnabled {
template_name: "GLOBAL_STATE".to_string(),
feature_name: "request_state".to_string(),
}
.into())
}
}
#[cfg(engine)]
pub async fn amalgamate_states(
&self,
build_state: TemplateState,
request_state: TemplateState,
) -> Result<TemplateState, ServerError> {
if let Some(amalgamate_states) = &self.amalgamation {
amalgamate_states.call(build_state, request_state).await
} else {
Err(BuildError::TemplateFeatureNotEnabled {
template_name: "GLOBAL_STATE".to_string(),
feature_name: "amalgamate_states".to_string(),
}
.into())
}
}
#[cfg(engine)]
pub fn uses_request_state(&self) -> bool {
self.request.is_some()
}
#[cfg(engine)]
pub fn uses_build_state(&self) -> bool {
self.build.is_some()
}
#[cfg(engine)]
pub fn can_amalgamate_states(&self) -> bool {
self.amalgamation.is_some()
}
}
#[derive(Clone)]
pub struct GlobalState(pub Rc<RefCell<GlobalStateType>>);
impl GlobalState {
pub(crate) fn new(ty: GlobalStateType) -> Self {
Self(Rc::new(RefCell::new(ty)))
}
}
#[derive(Debug)]
pub enum GlobalStateType {
Loaded(Box<dyn AnyFreeze>),
Server(TemplateState),
None,
}
impl GlobalStateType {
pub fn parse_active<S>(&self) -> Result<Option<S::Rx>, ClientError>
where
S: MakeRx,
S::Rx: MakeUnrx<Unrx = S> + AnyFreeze + Clone,
{
match &self {
Self::Loaded(any) => {
let rx = any
.as_any()
.downcast_ref::<S::Rx>()
.ok_or(ClientInvariantError::GlobalStateDowncast)?
.clone();
Ok(Some(rx))
}
Self::Server(_) | Self::None => Ok(None),
}
}
}
impl std::fmt::Debug for GlobalState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GlobalState").finish()
}
}
#[cfg(any(client, doc))]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum FrozenGlobalState {
Some(String),
Server,
None,
Used,
}
#[cfg(any(client, doc))]
impl From<&GlobalStateType> for FrozenGlobalState {
fn from(val: &GlobalStateType) -> Self {
match val {
GlobalStateType::Loaded(state) => Self::Some(state.freeze()),
GlobalStateType::None => Self::None,
GlobalStateType::Server(_) => Self::Server,
}
}
}