diff --git a/src/app.rs b/src/app.rs index 33e246e..ee948bf 100644 --- a/src/app.rs +++ b/src/app.rs @@ -173,10 +173,9 @@ async fn rss( let posts = posts .get_all_posts(|metadata, _| { - !query + query .tag - .as_ref() - .is_some_and(|tag| !metadata.tags.contains(tag)) + .as_ref().is_none_or(|tag| metadata.tags.contains(tag)) }) .await?; diff --git a/src/error.rs b/src/error.rs index 7790493..574f43e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,6 +4,7 @@ use askama_axum::Template; use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; use thiserror::Error; +use tracing::error; #[derive(Debug)] #[repr(transparent)] @@ -76,17 +77,14 @@ struct ErrorTemplate { impl IntoResponse for AppError { fn into_response(self) -> Response { + let error = self.to_string(); + error!("error while handling request: {error}"); + let status_code = match &self { AppError::PostError(PostError::NotFound(_)) => StatusCode::NOT_FOUND, AppError::RssDisabled => StatusCode::FORBIDDEN, _ => StatusCode::INTERNAL_SERVER_ERROR, }; - ( - status_code, - ErrorTemplate { - error: self.to_string(), - }, - ) - .into_response() + (status_code, ErrorTemplate { error }).into_response() } } diff --git a/src/hash_arc_store.rs b/src/hash_arc_store.rs deleted file mode 100644 index 90b35b4..0000000 --- a/src/hash_arc_store.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::hash::{DefaultHasher, Hash, Hasher}; -use std::marker::PhantomData; -use std::sync::Arc; - -pub struct HashArcStore -where - Lookup: Hash, -{ - inner: Option>, - hash: Option, - _phantom: PhantomData, -} - -impl HashArcStore -where - Lookup: Hash, -{ - pub fn new() -> Self { - Self { - inner: None, - hash: None, - _phantom: PhantomData, - } - } - - pub fn get_or_init(&mut self, key: &Lookup, init: impl Fn(&Lookup) -> Arc) -> Arc { - let mut h = DefaultHasher::new(); - key.hash(&mut h); - let hash = h.finish(); - if !self.hash.is_some_and(|inner_hash| inner_hash == hash) { - self.inner = Some(init(key)); - self.hash = Some(hash); - } - // safety: please. - unsafe { self.inner.as_ref().unwrap_unchecked().clone() } - } -} diff --git a/src/main.rs b/src/main.rs index 03004c5..e9627aa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,6 @@ mod app; mod config; mod error; -mod hash_arc_store; mod helpers; mod markdown_render; mod platform; @@ -77,25 +76,19 @@ async fn main() -> eyre::Result<()> { let reg = Arc::new(RwLock::new(reg)); - let watcher_token = cancellation_token.child_token(); - - let posts = Arc::new(MarkdownPosts::new(Arc::clone(&config)).await?); - let state = AppState { - config: Arc::clone(&config), - posts: Arc::clone(&posts), - reg: Arc::clone(®), - }; - debug!("setting up watcher"); + let watcher_token = cancellation_token.child_token(); tasks.spawn( watch_templates( config.dirs.custom_templates.clone(), watcher_token.clone(), - reg, + reg.clone(), ) .instrument(info_span!("custom_template_watcher")), ); + let posts = Arc::new(MarkdownPosts::new(Arc::clone(&config)).await?); + if config.cache.enable && config.cache.cleanup { if let Some(millis) = config.cache.cleanup_interval { let posts = Arc::clone(&posts); @@ -117,6 +110,11 @@ async fn main() -> eyre::Result<()> { } } + let state = AppState { + config: Arc::clone(&config), + posts: Arc::clone(&posts), + reg: Arc::clone(®), + }; let app = app::new(&config).with_state(state.clone()); let listener = TcpListener::bind(socket_addr) diff --git a/src/markdown_render.rs b/src/markdown_render.rs index 7153b3c..3eff9b9 100644 --- a/src/markdown_render.rs +++ b/src/markdown_render.rs @@ -1,5 +1,5 @@ -use std::sync::{Arc, OnceLock, RwLock}; - +use color_eyre::eyre::{self, Context}; +use comrak::adapters::SyntaxHighlighterAdapter; use comrak::markdown_to_html_with_plugins; use comrak::plugins::syntect::{SyntectAdapter, SyntectAdapterBuilder}; use comrak::ComrakOptions; @@ -7,32 +7,26 @@ use comrak::RenderPlugins; use syntect::highlighting::ThemeSet; use crate::config::RenderConfig; -use crate::hash_arc_store::HashArcStore; -fn syntect_adapter(config: &RenderConfig) -> Arc { - static STATE: OnceLock>> = OnceLock::new(); - let lock = STATE.get_or_init(|| RwLock::new(HashArcStore::new())); - let mut guard = lock.write().unwrap(); - guard.get_or_init(config, build_syntect) -} - -fn build_syntect(config: &RenderConfig) -> Arc { +pub fn build_syntect(config: &RenderConfig) -> eyre::Result { let mut theme_set = if config.syntect.load_defaults { ThemeSet::load_defaults() } else { ThemeSet::new() }; if let Some(path) = config.syntect.themes_dir.as_ref() { - theme_set.add_from_folder(path).unwrap(); + theme_set + .add_from_folder(path) + .with_context(|| format!("failed to add themes from {path:?}"))?; } let mut builder = SyntectAdapterBuilder::new().theme_set(theme_set); if let Some(theme) = config.syntect.theme.as_ref() { builder = builder.theme(theme); } - Arc::new(builder.build()) + Ok(builder.build()) } -pub fn render(markdown: &str, config: &RenderConfig) -> String { +pub fn render(markdown: &str, syntect: Option<&dyn SyntaxHighlighterAdapter>) -> String { let mut options = ComrakOptions::default(); options.extension.table = true; options.extension.autolink = true; @@ -43,8 +37,7 @@ pub fn render(markdown: &str, config: &RenderConfig) -> String { options.extension.header_ids = Some(String::new()); let mut render_plugins = RenderPlugins::default(); - let syntect = syntect_adapter(config); - render_plugins.codefence_syntax_highlighter = Some(syntect.as_ref()); + render_plugins.codefence_syntax_highlighter = syntect; let plugins = comrak::PluginsBuilder::default() .render(render_plugins) diff --git a/src/post/markdown_posts.rs b/src/post/markdown_posts.rs index e43b33a..5ef0290 100644 --- a/src/post/markdown_posts.rs +++ b/src/post/markdown_posts.rs @@ -9,6 +9,7 @@ use std::time::SystemTime; use axum::http::HeaderValue; use chrono::{DateTime, Utc}; use color_eyre::eyre::{self, Context}; +use comrak::plugins::syntect::SyntectAdapter; use fronma::parser::{parse, ParsedData}; use serde::Deserialize; use tokio::fs; @@ -16,7 +17,7 @@ use tokio::io::AsyncReadExt; use tracing::{error, info, warn}; use crate::config::Config; -use crate::markdown_render::render; +use crate::markdown_render::{build_syntect, render}; use crate::post::cache::{load_cache, Cache, CACHE_VERSION}; use crate::post::{PostError, PostManager, PostMetadata, RenderStats, ReturnedPost}; use crate::systemtime_as_secs::as_secs; @@ -62,6 +63,7 @@ where { cache: Option, config: C, + syntect: SyntectAdapter, } impl MarkdownPosts @@ -69,7 +71,10 @@ where C: Deref, { pub async fn new(config: C) -> eyre::Result> { - if config.cache.enable { + let syntect = + build_syntect(&config.render).context("failed to create syntax highlighting engine")?; + + let cache = if config.cache.enable { if config.cache.persistence && tokio::fs::try_exists(&config.cache.file).await? { info!("loading cache from file"); let mut cache = load_cache(&config).await.unwrap_or_else(|err| { @@ -83,22 +88,19 @@ where cache = Default::default(); }; - Ok(Self { - cache: Some(cache), - config, - }) + Some(cache) } else { - Ok(Self { - cache: Some(Default::default()), - config, - }) + Some(Default::default()) } } else { - Ok(Self { - cache: None, - config, - }) - } + None + }; + + Ok(Self { + cache, + config, + syntect, + }) } async fn parse_and_render( @@ -126,7 +128,7 @@ where let parsing = parsing_start.elapsed(); let before_render = Instant::now(); - let post = render(body, &self.config.render); + let post = render(body, Some(&self.syntect)); let rendering = before_render.elapsed(); if let Some(cache) = self.cache.as_ref() { diff --git a/src/post/mod.rs b/src/post/mod.rs index 111b156..4ba51d4 100644 --- a/src/post/mod.rs +++ b/src/post/mod.rs @@ -58,7 +58,7 @@ pub trait PostManager { tag: Option<&String>, ) -> Result, PostError> { let mut posts = self - .get_all_post_metadata(|metadata| !tag.is_some_and(|tag| !metadata.tags.contains(tag))) + .get_all_post_metadata(|metadata| tag.is_none_or(|tag| metadata.tags.contains(tag))) .await?; // we still want some semblance of order if created_at is None so sort by mtime as well posts.sort_unstable_by_key(|metadata| metadata.modified_at.unwrap_or_default()); diff --git a/src/ranged_i128_visitor.rs b/src/ranged_i128_visitor.rs index e6350d0..8436fa4 100644 --- a/src/ranged_i128_visitor.rs +++ b/src/ranged_i128_visitor.rs @@ -1,5 +1,5 @@ pub struct RangedI128Visitor; -impl<'de, const START: i128, const END: i128> serde::de::Visitor<'de> +impl serde::de::Visitor<'_> for RangedI128Visitor { type Value = i128;