mod builder;
mod extras;
pub mod frontmatter;
mod link_list;
pub mod resource;
#[cfg(feature = "serve")]
pub mod serving;
mod util;
use std::{
collections::HashMap,
path::{Path, PathBuf},
};
use extras::ExtraData;
use eyre::Context;
use resource::{EmbedMetadata, ResourceBuilderConfig};
use serde::{Deserialize, Serialize};
use url::Url;
use walkdir::WalkDir;
use builder::SiteBuilder;
pub const PAGES_PATH: &str = "pages";
pub const TEMPLATES_PATH: &str = "templates";
pub const SASS_PATH: &str = "sass";
pub const ROOT_PATH: &str = "root";
pub const RESOURCES_PATH: &str = "resources";
#[derive(Debug, Serialize, Deserialize)]
pub struct SiteConfig {
pub base_url: Url,
pub title: String,
pub description: String,
pub theme_color: String,
pub build: Option<String>,
pub sass_styles: Vec<PathBuf>,
pub cdn_url: Url,
pub webdog_path: Option<String>,
pub code_theme: String,
pub resources: HashMap<String, ResourceBuilderConfig>,
}
impl SiteConfig {
pub const FILENAME: &str = "config.yaml";
pub fn new(base_url: Url, cdn_url: Url, title: String) -> Self {
Self {
base_url,
title,
description: Default::default(),
theme_color: "#ffc4fc".to_string(),
build: None,
sass_styles: vec!["index.scss".into()],
cdn_url,
webdog_path: None,
code_theme: "base16-ocean.dark".to_string(),
resources: Default::default(),
}
}
pub fn cdn_url(&self, file: &str) -> eyre::Result<Url> {
Ok(self.cdn_url.join(file)?)
}
pub fn check(&self, builder: &SiteBuilder) -> eyre::Result<()> {
builder
.theme_set
.themes
.contains_key(&self.code_theme)
.then_some(())
.ok_or_else(|| eyre::eyre!("missing code theme: {}", self.code_theme))?;
Ok(())
}
pub fn read(site_path: &Path) -> eyre::Result<Self> {
let config_path = site_path.join(SiteConfig::FILENAME);
if !config_path.exists() {
eyre::bail!("no site config found!");
}
Ok(serde_yaml_ng::from_str(&std::fs::read_to_string(
config_path,
)?)?)
}
}
#[derive(Debug, Default, Deserialize)]
pub struct TemplateMetadata {}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct PageMetadata {
pub title: Option<String>,
pub template: Option<String>,
#[serde(default)]
pub embed: Option<EmbedMetadata>,
#[serde(default)]
pub scripts: Vec<String>,
#[serde(default)]
pub styles: Vec<String>,
#[serde(default)]
pub extra: Option<ExtraData>,
#[serde(default)]
pub userdata: serde_yaml_ng::Value,
#[serde(skip)]
pub is_partial: bool,
}
#[derive(Debug)]
pub struct Site {
pub site_path: PathBuf,
pub config: SiteConfig,
pub page_index: HashMap<String, PathBuf>,
}
impl Site {
pub fn new(site_path: &Path) -> eyre::Result<Self> {
let config = SiteConfig::read(site_path)?;
let mut page_index = HashMap::new();
let pages_path = site_path.join(PAGES_PATH);
for entry in WalkDir::new(&pages_path).into_iter() {
let entry = entry.wrap_err("Failed to read page entry")?;
let path = entry.path();
if let Some(ext) = path.extension()
&& ext == "md"
&& entry.file_type().is_file()
{
page_index.insert(
path.strip_prefix(&pages_path)
.wrap_err("This really shouldn't have happened")?
.with_extension("")
.to_string_lossy()
.to_string(),
path.to_owned(),
);
}
}
Ok(Self {
site_path: site_path.to_owned(),
config,
page_index,
})
}
pub fn build_once(self) -> eyre::Result<()> {
SiteBuilder::new(self, false)?.prepare()?.build_all()
}
}