165 lines
4.4 KiB
Rust
165 lines
4.4 KiB
Rust
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)?,
|
|
)
|
|
}
|
|
}
|