use std::{
borrow::Cow,
fs, io,
path::{Path, PathBuf},
sync::Arc,
};
use crate::{
Blog,
serve::{Feed, Serve},
};
#[derive(Clone, Debug, Default)]
pub struct Config {
pub name: String,
pub url: String,
pub template_srcs: Vec<TemplateSrc>,
pub authors: Vec<Author>,
pub serves: Vec<Arc<dyn Serve>>,
pub feeds: Vec<Feed>,
}
impl Config {
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = name.into();
self
}
pub fn url(mut self, url: impl Into<String>) -> Self {
self.url = url.into();
self
}
pub fn template_str(
mut self,
name: impl Into<Cow<'static, str>>,
body: impl Into<Cow<'static, str>>,
) -> Self {
self.template_srcs.push(TemplateSrc::InMem {
name: name.into(),
body: body.into(),
});
self
}
pub fn template_file(mut self, path: impl AsRef<Path>) -> Self {
self.template_srcs.push(TemplateSrc::Stored {
name: None,
path: path.as_ref().to_path_buf(),
});
self
}
pub fn template_file_named(
mut self,
name: impl Into<Cow<'static, str>>,
path: impl AsRef<Path>,
) -> Self {
self.template_srcs.push(TemplateSrc::Stored {
name: Some(name.into()),
path: path.as_ref().to_path_buf(),
});
self
}
pub fn author(mut self, author: Author) -> Self {
self.authors.push(author);
self
}
pub fn authors(mut self, authors: impl IntoIterator<Item = Author>) -> Self {
self.authors.extend(authors);
self
}
pub fn serve(mut self, serve: impl Serve + 'static) -> Self {
self.serves.push(Arc::new(serve));
self
}
pub fn serves(mut self, serves: impl IntoIterator<Item = impl Serve + 'static>) -> Self {
for serve in serves {
self.serves.push(Arc::new(serve));
}
self
}
pub fn feed(mut self, feed: Feed) -> Self {
self.feeds.push(feed);
self
}
pub fn build(self, dir: &Path) -> io::Result<Blog> {
Blog::load(self, dir)
}
}
#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct Author {
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub website: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
}
impl Author {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
..Default::default()
}
}
pub fn uri(mut self, uri: impl Into<String>) -> Self {
self.website = Some(uri.into());
self
}
pub fn email(mut self, email: impl Into<String>) -> Self {
self.email = Some(email.into());
self
}
}
#[derive(Clone, Debug)]
pub enum TemplateSrc {
InMem {
name: Cow<'static, str>,
body: Cow<'static, str>,
},
Stored {
name: Option<Cow<'static, str>>,
path: PathBuf,
},
}
impl TemplateSrc {
pub fn add_to(self, dir: &Path, env: &mut minijinja::Environment) -> io::Result<()> {
match self {
Self::InMem {
name,
body,
} => env.add_template_owned(name, body),
Self::Stored {
name,
path: body,
} => {
let name = name.unwrap_or_else(|| {
let mut name = body.file_name().unwrap().to_str().unwrap();
for ext in [".t.html", ".jinja.html", ".html", ".html.jinja", ".jinja"] {
if let Some(n) = name.strip_suffix(ext) {
name = n;
break;
}
}
name.to_string().into()
});
let contents = fs::read_to_string(dir.join(body))?;
env.add_template_owned(name, contents)
}
}
.map_err(|e| io::Error::other(format!("{e}")))
}
}