make cache optional

This commit is contained in:
slonkazoid 2024-04-20 20:59:00 +03:00
parent 58c8021864
commit 18385d3e57
Signed by: slonk
SSH key fingerprint: SHA256:tbZfJX4IOvZ0LGWOWu5Ijo8jfMPi78TU7x1VoEeCIjM
4 changed files with 105 additions and 62 deletions

View file

@ -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`)

View file

@ -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<PathBuf>,
pub theme: Option<String>,
}
#[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<PathBuf>,
}
#[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<PathBuf>,
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<Config> {
let config_file = env::var(format!("{}_CONFIG", env!("CARGO_BIN_NAME")))
.unwrap_or(String::from("config.toml"));

View file

@ -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, color_eyre::Report>(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, color_eyre::Report>(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

View file

@ -75,7 +75,7 @@ pub enum RenderStats {
#[derive(Clone)]
pub struct PostManager {
dir: PathBuf,
cache: Cache,
cache: Option<Cache>,
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<Cache> {
self.cache
}
}