use tokio::sync::{Notify, RwLock, broadcast};
use crate::render::BuiltSite;
#[doc(hidden)]
pub struct AppState {
pub(crate) site: RwLock<BuiltSite>,
pub reload_tx: broadcast::Sender<()>,
pub(crate) shutdown: Notify,
}
impl AppState {
pub(super) fn new(rendered: BuiltSite) -> Self {
let (reload_tx, _) = broadcast::channel(16);
Self {
site: RwLock::new(rendered),
reload_tx,
shutdown: Notify::new(),
}
}
pub(crate) async fn swap(&self, rendered: BuiltSite) {
*self.site.write().await = rendered;
let _ = self.reload_tx.send(());
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
fn empty_rendered() -> BuiltSite {
BuiltSite {
pages: HashMap::new(),
not_found_html: String::new(),
root_files: Vec::new(),
diagnostics: Default::default(),
}
}
fn rendered_with_page(url: &str, html: &str) -> BuiltSite {
let mut pages = HashMap::new();
pages.insert(url.to_string(), html.to_string());
BuiltSite {
pages,
not_found_html: String::new(),
root_files: Vec::new(),
diagnostics: Default::default(),
}
}
#[tokio::test]
async fn swap_replaces_site() {
let state = AppState::new(empty_rendered());
assert!(state.site.read().await.lookup("/blog/post/").is_none());
state
.swap(rendered_with_page("/blog/post/", "<p>hi</p>"))
.await;
assert_eq!(
state.site.read().await.lookup("/blog/post/"),
Some("<p>hi</p>")
);
}
#[tokio::test]
async fn swap_broadcasts_reload() {
let state = AppState::new(empty_rendered());
let mut rx = state.reload_tx.subscribe();
state.swap(empty_rendered()).await;
assert!(rx.try_recv().is_ok());
}
}