use crate::config::Config;
use core::net::{IpAddr, SocketAddr};
use include_dir::{Dir, include_dir};
use parking_lot::RwLock;
use std::{
collections::HashMap,
sync::Arc,
};
use tera::{Context, Tera};
use terracotta::{
app::{
config::HtmlTemplates,
errors::AppError,
init::setup_tera,
state::StateProvider as AppStateProvider,
utility::render,
},
assets::{
config::Config as AssetsConfig,
state::StateProvider as AssetsStateProvider,
},
auth::state::StateProvider as AuthStateProvider,
stats::{
config::Config as StatsConfig,
state::{State as StatsState, StateProvider as StatsStateProvider},
},
};
use tokio::sync::RwLock as AsyncRwLock;
#[derive(Debug)]
pub struct AppState {
pub address: RwLock<Option<SocketAddr>>,
pub assets_dir: Arc<Dir<'static>>,
pub config: Config,
pub content_dir: Arc<Dir<'static>>,
pub stats: AsyncRwLock<StatsState>,
pub tera: Tera,
}
impl AppState {
pub fn new(config: Config) -> Self {
Self {
config,
..Default::default()
}
}
}
impl AppStateProvider for AppState {
fn address(&self) -> Option<SocketAddr> {
*self.address.read()
}
fn host(&self) -> IpAddr {
self.config.host
}
fn html_templates_config(&self) -> &HtmlTemplates {
&self.config.html
}
fn port(&self) -> u16 {
self.config.port
}
async fn render<T: AsRef<str> + Send>(&self, template: T, context: &Context) -> Result<String, AppError> {
render(self, template.as_ref(), context).await
}
fn set_address(&self, address: Option<SocketAddr>) {
*self.address.write() = address;
}
fn tera(&self) -> &Tera {
&self.tera
}
fn title(&self) -> &String {
&self.config.title
}
}
impl AssetsStateProvider for AppState {
fn config(&self) -> &AssetsConfig {
&self.config.assets
}
fn assets_dir(&self) -> Arc<Dir<'static>> {
Arc::clone(&self.assets_dir)
}
fn content_dir(&self) -> Arc<Dir<'static>> {
Arc::clone(&self.content_dir)
}
}
impl AuthStateProvider for AppState {
fn users(&self) -> &HashMap<String, String> {
&self.config.users
}
}
impl Default for AppState {
fn default() -> Self {
Self {
address: RwLock::new(None),
assets_dir: Arc::new(include_dir!("static")),
config: Config::default(),
content_dir: Arc::new(include_dir!("$OUT_DIR")),
stats: AsyncRwLock::new(StatsState::default()),
tera: setup_tera(&Arc::new(include_dir!("html")))
.expect("Error loading templates")
,
}
}
}
impl StatsStateProvider for AppState {
fn config(&self) -> &StatsConfig {
&self.config.stats
}
fn state(&self) -> &AsyncRwLock<StatsState> {
&self.stats
}
}