Module restructure

This commit is contained in:
Olivia Brooks
2025-10-15 12:40:52 -04:00
parent 3937777255
commit 90ceb2124e
5 changed files with 70 additions and 46 deletions

164
src/blog/meta.rs Normal file
View File

@@ -0,0 +1,164 @@
use std::{
fs::File,
path::{Path, PathBuf},
};
use ron::error::SpannedResult;
use serde::{Deserialize, de::DeserializeOwned};
use super::{
og,
res::{Date, Image},
};
use crate::error;
pub static FALLBACK_META_PATH: std::sync::OnceLock<PathBuf> = std::sync::OnceLock::new();
static FALLBACK_META: std::sync::OnceLock<MetaFallback> = std::sync::OnceLock::new();
/// Fallback Meta Setup
pub fn load_fb_meta<P: AsRef<Path>>(path: P) {
FALLBACK_META_PATH.get_or_init(|| path.as_ref().to_path_buf());
// Trigger OnceLock loading of fallback_meta path.
Meta::default();
}
/// Make sure to populate `FALLBACK_META_PATH` before constructing a `Meta`.
///
/// `MetaFallback` is the same as `Meta`, but without a `Default` impl.
#[derive(Clone, Debug, Deserialize)]
#[serde(default)]
pub struct Meta {
title: String,
description: String,
tags: Vec<String>,
site_name: String,
locale: String,
// `type` is a keyword, this gets around that limitation.
//
// Trailing underscore is apparently a semi-common practice for this, as
// prefixed underscore is reserved by the compiler to indicate
// intentionally unused assignments.
#[serde(rename = "type")]
type_: String,
authors: Vec<og::Author>,
date: Date,
image: Option<Image>,
}
impl Default for Meta {
fn default() -> Self {
// Get FALLBACK_META, loading it from FALLBACK_META_PATH if OnceLock not initialized.
FALLBACK_META
.get_or_init(|| {
MetaFallback::try_from(FALLBACK_META_PATH.get().unwrap().as_path())
.expect("Failed to deserialize fallback_meta file")
})
.to_owned()
.as_meta()
}
}
/// Same as `Meta`, but acts as the fallback for Meta's default values.
///
/// Not intended to be directly constructed; populate `FALLBACK_META_PATH`
/// before constructing a `Meta`.
#[derive(Debug, Deserialize)]
struct MetaFallback {
title: String,
description: String,
tags: Vec<String>,
site_name: String,
locale: String,
#[serde(rename = "type")]
type_: String, // `type` is a keyword, this gets around that limitation.
authors: Vec<og::Author>,
date: Date,
image: Option<Image>,
}
impl MetaFallback {
fn as_meta(&self) -> Meta {
// This is just to skip some `self.` spam.
// There doesn't seem to be a better way to do this.
let MetaFallback {
title,
description,
tags,
site_name,
locale,
type_,
authors,
date,
image,
} = self;
Meta {
title: title.to_string(),
description: description.to_string(),
tags: tags.to_owned(),
site_name: site_name.to_string(),
locale: locale.to_string(),
type_: type_.to_string(),
authors: authors.to_owned(),
date: date.to_owned(),
image: image.to_owned(),
}
}
}
fn meta_try_from_file<T: DeserializeOwned>(file: File) -> SpannedResult<T> {
use ron::extensions::Extensions;
ron::Options::default()
//.with_default_extension(Extensions::EXPLICIT_STRUCT_NAMES)
.with_default_extension(Extensions::IMPLICIT_SOME)
.with_default_extension(Extensions::UNWRAP_NEWTYPES)
.with_default_extension(Extensions::UNWRAP_VARIANT_NEWTYPES)
.from_reader(file)
}
// TryFrom<File>
impl TryFrom<File> for Meta {
type Error = error::MetaError;
fn try_from(file: File) -> Result<Self, Self::Error> {
Ok(meta_try_from_file(file)?)
}
}
impl TryFrom<File> for MetaFallback {
type Error = error::MetaError;
fn try_from(file: File) -> Result<Self, Self::Error> {
Ok(meta_try_from_file(file)?)
}
}
// TryFrom<&Path>
impl TryFrom<&Path> for Meta {
type Error = error::MetaError;
fn try_from(path: &Path) -> Result<Self, Self::Error> {
Self::try_from(
std::fs::OpenOptions::new()
.read(true) // Just ensure that file opens read-only.
.open(path)?,
)
}
}
impl TryFrom<&Path> for MetaFallback {
type Error = error::MetaError;
fn try_from(path: &Path) -> Result<Self, Self::Error> {
Self::try_from(
std::fs::OpenOptions::new()
.read(true) // Just ensure that file opens read-only.
.open(path)?,
)
}
}