mod build;
mod build_error_page;
mod export;
mod export_error_page;
mod serve;
mod server;
mod tinker;
pub use server::{ApiResponse, SubsequentLoadQueryParams};
use crate::{
error_views::ErrorViews,
errors::*,
i18n::{Locales, TranslationsManager},
init::{PerseusAppBase, Tm},
plugins::Plugins,
server::HtmlShell,
state::{GlobalStateCreator, TemplateState},
stores::{ImmutableStore, MutableStore},
template::EntityMap,
};
use futures::executor::block_on;
use std::{collections::HashMap, path::PathBuf, sync::Arc};
use sycamore::web::SsrNode;
#[derive(Debug)]
pub struct Turbine<M: MutableStore, T: TranslationsManager> {
entities: EntityMap<SsrNode>,
error_views: Arc<ErrorViews<SsrNode>>,
locales: Locales,
immutable_store: ImmutableStore,
mutable_store: M,
translations_manager: T,
global_state_creator: Arc<GlobalStateCreator>,
plugins: Arc<Plugins>,
index_view_str: String,
root_id: String,
pub static_dir: PathBuf,
pub static_aliases: HashMap<String, String>,
render_cfg: HashMap<String, String>,
global_states_by_locale: HashMap<String, TemplateState>,
html_shell: Option<HtmlShell>,
}
impl<M: MutableStore, T: TranslationsManager> TryFrom<PerseusAppBase<SsrNode, M, T>>
for Turbine<M, T>
{
type Error = PluginError;
fn try_from(app: PerseusAppBase<SsrNode, M, T>) -> Result<Self, Self::Error> {
let locales = app.get_locales()?;
let immutable_store = app.get_immutable_store()?;
let index_view_str = app.get_index_view_str();
let root_id = app.get_root()?;
let static_aliases = app.get_static_aliases()?;
Ok(Self {
entities: app.entities,
locales,
immutable_store,
mutable_store: app.mutable_store,
global_state_creator: app.global_state_creator,
plugins: app.plugins,
index_view_str,
root_id,
static_dir: PathBuf::from(&app.static_dir),
static_aliases,
#[cfg(debug_assertions)]
error_views: app.error_views.unwrap_or_default(),
#[cfg(not(debug_assertions))]
error_views: app
.error_views
.expect("you must provide your own error pages in production"),
translations_manager: match app.translations_manager {
Tm::Dummy(tm) => tm,
Tm::Full(tm) => block_on(tm),
},
render_cfg: HashMap::new(),
global_states_by_locale: HashMap::new(),
html_shell: None,
})
}
}
impl<M: MutableStore, T: TranslationsManager> Turbine<M, T> {
pub async fn populate_after_build(&mut self) -> Result<(), ServerError> {
let render_cfg_str = self.immutable_store.read("render_conf.json").await?;
let render_cfg = serde_json::from_str::<HashMap<String, String>>(&render_cfg_str)
.map_err(|err| ServerError::BuildError(BuildError::RenderCfgInvalid { source: err }))?;
self.render_cfg = render_cfg;
let mut global_states_by_locale = HashMap::new();
for locale in self.locales.get_all() {
let res = self
.immutable_store
.read(&format!("static/global_state_{}.json", &locale))
.await;
let global_state = match res {
Ok(state) => TemplateState::from_str(&state)
.map_err(|err| ServerError::InvalidPageState { source: err })?,
Err(StoreError::NotFound { .. }) => TemplateState::empty(),
Err(err) => return Err(err.into()),
};
global_states_by_locale.insert(locale.to_string(), global_state);
}
self.global_states_by_locale = global_states_by_locale;
let html_shell = PerseusAppBase::<SsrNode, M, T>::get_html_shell(
self.index_view_str.to_string(),
&self.root_id,
&self.render_cfg,
&self.plugins,
)
.await?;
self.html_shell = Some(html_shell);
Ok(())
}
}