From 18385d3e5725f78e62b299342207159681a31fa8 Mon Sep 17 00:00:00 2001 From: slonkazoid Date: Sat, 20 Apr 2024 20:59:00 +0300 Subject: [PATCH] make cache optional --- README.md | 10 ++++--- src/config.rs | 33 ++++++++++++++++------- src/main.rs | 72 ++++++++++++++++++++++++++++--------------------- src/post/mod.rs | 52 +++++++++++++++++++++-------------- 4 files changed, 105 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index f065b99..371760b 100644 --- a/README.md +++ b/README.md @@ -38,11 +38,15 @@ port = 3000 # port to listen on title = "bingus-blog" # title of the website description = "blazingly fast markdown blog software written in rust memory safe" # description of the website posts_dir = "posts" # where posts are stored -#cache_file = "..." # file to serialize the cache into on shutdown, and - # to deserialize from on startup. uncomment to enable markdown_access = true # allow users to see the raw markdown of a post -[render] # rendering-specific settings +[cache] # cache settings +enable = true # save metadata and rendered posts into RAM + # highly recommended, only turn off if asolutely necessary +#persistence = "..." # file to save the cache to on shutdown, and + # to load from on startup. uncomment to enable + +[render] # post rendering settings syntect.load_defaults = false # include default syntect themes syntect.themes_dir = "themes" # directory to include themes from syntect.theme = "Catppuccin Mocha" # theme file name (without `.tmTheme`) diff --git a/src/config.rs b/src/config.rs index 6ed5327..1fc642f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,13 +10,14 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tracing::{error, info}; #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] +#[serde(default)] pub struct SyntectConfig { pub load_defaults: bool, pub themes_dir: Option, pub theme: Option, } -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, Default)] #[serde(default)] pub struct RenderConfig { pub syntect: SyntectConfig, @@ -30,6 +31,13 @@ pub struct PrecompressionConfig { pub watch: bool, } +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(default)] +pub struct CacheConfig { + pub enable: bool, + pub persistence: Option, +} + #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(default)] pub struct Config { @@ -41,7 +49,7 @@ pub struct Config { pub render: RenderConfig, #[cfg(feature = "precompression")] pub precompression: PrecompressionConfig, - pub cache_file: Option, + pub cache: CacheConfig, pub markdown_access: bool, } @@ -56,20 +64,18 @@ impl Default for Config { posts_dir: "posts".into(), #[cfg(feature = "precompression")] precompression: Default::default(), - cache_file: None, + cache: Default::default(), markdown_access: true, } } } -impl Default for RenderConfig { +impl Default for SyntectConfig { fn default() -> Self { Self { - syntect: SyntectConfig { - load_defaults: false, - themes_dir: Some("themes".into()), - theme: Some("Catppuccin Mocha".into()), - }, + load_defaults: false, + themes_dir: Some("themes".into()), + theme: Some("Catppuccin Mocha".into()), } } } @@ -84,6 +90,15 @@ impl Default for PrecompressionConfig { } } +impl Default for CacheConfig { + fn default() -> Self { + Self { + enable: true, + persistence: None, + } + } +} + pub async fn load() -> Result { let config_file = env::var(format!("{}_CONFIG", env!("CARGO_BIN_NAME"))) .unwrap_or(String::from("config.toml")); diff --git a/src/main.rs b/src/main.rs index 1d07e44..4b8acf3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -189,37 +189,45 @@ async fn main() -> eyre::Result<()> { } } - let posts = if let Some(path) = config.cache_file.as_ref() - && tokio::fs::try_exists(&path) - .await - .with_context(|| format!("failed to check if {} exists", path.display()))? - { - info!("loading cache from file"); - let load_cache = async { - let mut cache_file = tokio::fs::File::open(&path) + let posts = if config.cache.enable { + if let Some(path) = config.cache.persistence.as_ref() + && tokio::fs::try_exists(&path) .await - .context("failed to open cache file")?; - let mut serialized = Vec::with_capacity(4096); - cache_file - .read_to_end(&mut serialized) - .await - .context("failed to read cache file")?; - let cache = - bitcode::deserialize(serialized.as_slice()).context("failed to parse cache")?; - Ok::(PostManager::new_with_cache( + .with_context(|| format!("failed to check if {} exists", path.display()))? + { + info!("loading cache from file"); + let load_cache = async { + let mut cache_file = tokio::fs::File::open(&path) + .await + .context("failed to open cache file")?; + let mut serialized = Vec::with_capacity(4096); + cache_file + .read_to_end(&mut serialized) + .await + .context("failed to read cache file")?; + let cache = + bitcode::deserialize(serialized.as_slice()).context("failed to parse cache")?; + Ok::(PostManager::new_with_cache( + config.posts_dir.clone(), + config.render.clone(), + cache, + )) + } + .await; + match load_cache { + Ok(posts) => posts, + Err(err) => { + error!("failed to load cache: {}", err); + info!("using empty cache"); + PostManager::new(config.posts_dir.clone(), config.render.clone()) + } + } + } else { + PostManager::new_with_cache( config.posts_dir.clone(), config.render.clone(), - cache, - )) - } - .await; - match load_cache { - Ok(posts) => posts, - Err(err) => { - error!("failed to load cache: {}", err); - info!("using empty cache"); - PostManager::new(config.posts_dir.clone(), config.render.clone()) - } + Default::default(), + ) } } else { PostManager::new(config.posts_dir.clone(), config.render.clone()) @@ -321,8 +329,12 @@ async fn main() -> eyre::Result<()> { warn!("couldn't unwrap Arc over AppState, more than one strong reference exists for Arc. cloning instead"); AppState::clone(state.as_ref()) }); - if let Some(path) = config.cache_file.as_ref() { - let cache = posts.into_cache(); + if config.cache.enable + && let Some(path) = config.cache.persistence.as_ref() + { + let cache = posts + .into_cache() + .unwrap_or_else(|| unreachable!("cache should always exist in this state")); let mut serialized = bitcode::serialize(&cache).context("failed to serialize cache")?; let mut cache_file = tokio::fs::File::create(path) .await diff --git a/src/post/mod.rs b/src/post/mod.rs index a833b95..f61fc68 100644 --- a/src/post/mod.rs +++ b/src/post/mod.rs @@ -75,7 +75,7 @@ pub enum RenderStats { #[derive(Clone)] pub struct PostManager { dir: PathBuf, - cache: Cache, + cache: Option, config: RenderConfig, } @@ -83,13 +83,17 @@ impl PostManager { pub fn new(dir: PathBuf, config: RenderConfig) -> PostManager { PostManager { dir, - cache: Default::default(), + cache: None, config, } } pub fn new_with_cache(dir: PathBuf, config: RenderConfig, cache: Cache) -> PostManager { - PostManager { dir, cache, config } + PostManager { + dir, + cache: Some(cache), + config, + } } async fn parse_and_render( @@ -125,19 +129,21 @@ impl PostManager { .render()?; let rendering = before_render.elapsed(); - self.cache - .insert( - name.to_string(), - metadata.clone(), - modified - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs(), - post.clone(), - &self.config, - ) - .await - .unwrap_or_else(|err| warn!("failed to insert {:?} into cache", err.0)); + if let Some(cache) = self.cache.as_ref() { + cache + .insert( + name.to_string(), + metadata.clone(), + modified + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs(), + post.clone(), + &self.config, + ) + .await + .unwrap_or_else(|err| warn!("failed to insert {:?} into cache", err.0)) + }; Ok((metadata, post, (parsing, rendering))) } @@ -166,7 +172,9 @@ impl PostManager { .to_string_lossy() .to_string(); - if let Some(hit) = self.cache.lookup_metadata(&name, mtime).await { + if let Some(cache) = self.cache.as_ref() + && let Some(hit) = cache.lookup_metadata(&name, mtime).await + { posts.push(hit) } else if let Ok((metadata, ..)) = self.parse_and_render(name, path).await { posts.push(metadata); @@ -194,7 +202,9 @@ impl PostManager { Ok(value) => value, Err(err) => match err.kind() { io::ErrorKind::NotFound => { - self.cache.remove(name).await; + if let Some(cache) = self.cache.as_ref() { + cache.remove(name).await; + } return Err(PostError::NotFound(name.to_string())); } _ => return Err(PostError::IoError(err)), @@ -206,7 +216,9 @@ impl PostManager { .unwrap() .as_secs(); - if let Some(hit) = self.cache.lookup(name, mtime, &self.config).await { + if let Some(cache) = self.cache.as_ref() + && let Some(hit) = cache.lookup(name, mtime, &self.config).await + { Ok(( hit.metadata, hit.rendered, @@ -222,7 +234,7 @@ impl PostManager { } } - pub fn into_cache(self) -> Cache { + pub fn into_cache(self) -> Option { self.cache } }