Module restructure
This commit is contained in:
164
src/blog/meta.rs
Normal file
164
src/blog/meta.rs
Normal 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)?,
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user