Skip to content
This repository has been archived by the owner on Jan 4, 2024. It is now read-only.

Commit

Permalink
allow regular pages to have fm
Browse files Browse the repository at this point in the history
  • Loading branch information
Sky9x committed Nov 22, 2023
1 parent 6c7e7cd commit 7d0c9bd
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 61 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ anyhow = "1"
semver = { version = "1", features = ["serde"] }

pulldown-cmark = "0.9"
pulldown-cmark-to-cmark = "11.0.1"
pulldown-cmark-to-cmark = "11"

serde = { version = "1", features = ["derive"] }
serde_yaml = "0.9"
Expand Down
6 changes: 5 additions & 1 deletion src/front_matter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ use anyhow::{anyhow, bail, ensure, Result};
use serde::{Deserialize, Serialize};
use serde_yaml::{Mapping, Value};

#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, Default)]
#[serde(deny_unknown_fields)]
pub struct FrontMatter {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,

// converts absent to/from an empty vec
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub nav: Vec<NavElem>,
}

Expand Down
12 changes: 9 additions & 3 deletions src/md.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ pub fn extract_title_h1(content: &str) -> Result<String> {
Ok(title)
}

pub fn take_front_matter(content: &str) -> Result<(Option<FrontMatter>, &str)> {
pub fn take_front_matter(content: &str) -> Result<(FrontMatter, &str)> {
let Some(s) = content.strip_prefix("---") else {
return Ok((None, content));
return Ok((FrontMatter::default(), content));
};

let Some((fm, remaining)) = s.split_once("\n---") else {
Expand All @@ -103,5 +103,11 @@ pub fn take_front_matter(content: &str) -> Result<(Option<FrontMatter>, &str)> {
pub fn prepend_front_matter(fm: &FrontMatter, content: &str) -> String {
let fm_yaml = serde_yaml::to_string(fm).expect("ser error????");

format!("---\n{fm_yaml}---\n{content}")
// FIXME use a better check
if fm_yaml.trim() == "{}" {
// dont put an empty fm block
content.to_string()
} else {
format!("---\n{fm_yaml}---\n{content}")
}
}
19 changes: 3 additions & 16 deletions src/nav.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,19 @@ pub struct NavPage {
pub path: Path,
pub name: String,

pub fm: FrontMatter,

pub raw_content: String,
pub built_content: String,
pub fixed_content: String,
}

/// An index page (index.md)
///
/// name is page.name
#[derive(Debug)]
pub struct NavIndex {
pub page: NavPage,
pub fm: FrontMatter,
}

/// A folder with an index
///
/// name is index.page.name (ignored for root)
#[derive(Debug)]
pub struct NavFolder {
pub index: NavIndex,
pub index: NavPage,
pub children: Vec<NavItem>,
}

Expand Down Expand Up @@ -60,12 +53,6 @@ impl ForEachPage for NavPage {
}
}

impl ForEachPage for NavIndex {
fn for_each_page(&self, f: &mut impl FnMut(&NavPage) -> Result<()>) -> Result<()> {
self.page.for_each_page(f)
}
}

impl ForEachPage for NavItem {
fn for_each_page(&self, f: &mut impl FnMut(&NavPage) -> Result<()>) -> Result<()> {
match self {
Expand Down
73 changes: 33 additions & 40 deletions src/process.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
use crate::front_matter::{FrontMatter, NavElem};
use crate::front_matter::NavElem;
use crate::md;
use crate::nav::{NavCategory, NavFolder, NavIndex, NavItem, NavPage};
use crate::nav::{NavCategory, NavFolder, NavItem, NavPage};
use crate::path::Path;
use anyhow::{bail, ensure, Result};
use anyhow::{anyhow, bail, ensure, Result};
use std::fs;

pub fn process_item(elem: &NavElem, dir: &Path) -> Result<NavItem> {
match elem {
NavElem::File { name, path } => {
process_page(&dir.join(path), name.clone()).map(NavItem::Page)
// regular pages can't have nav
process_page(&dir.join(path), name.clone())
.and_then(ensure_page_has_no_nav)
.map(NavItem::Page)
}
NavElem::Folder { name, path } => {
process_folder(&dir.join(path), name.clone()).map(NavItem::Folder)
Expand All @@ -22,17 +25,14 @@ pub fn process_item(elem: &NavElem, dir: &Path) -> Result<NavItem> {
}
}

fn process_content(path: &Path, name: Option<String>) -> Result<(NavPage, Option<FrontMatter>)> {
fn process_page(path: &Path, name: Option<String>) -> Result<NavPage> {
let raw = unwrap!(fs::read_to_string(path), "could not read file {path}",);

let (fm, content) = md::take_front_matter(&raw)?;
let (fm, content) = unwrap!(md::take_front_matter(&raw), "invalid fm in {path}");

let built_content = md::build(content);
let mut fixed_content = md::fix(content);

if let Some(fm) = &fm {
fixed_content = md::prepend_front_matter(fm, &fixed_content);
}
let fixed_content = md::fix(content);
let fixed_content = md::prepend_front_matter(&fm, &fixed_content);

// enforce all files having a title
let title_h1 = unwrap!(
Expand All @@ -42,7 +42,7 @@ fn process_content(path: &Path, name: Option<String>) -> Result<(NavPage, Option

// if fm specifies a name, use it over an assigned name
// this is mainly only useful for the root index.md
let name = if let Some(fm_name) = fm.as_ref().and_then(|fm| fm.name.as_ref()) {
let name = if let Some(fm_name) = fm.name.as_ref() {
ensure!(
name.is_none(),
"cannot specify both a fm name and a nav name for {path}"
Expand All @@ -53,40 +53,24 @@ fn process_content(path: &Path, name: Option<String>) -> Result<(NavPage, Option
name.unwrap_or(title_h1)
};

Ok((
NavPage {
path: path.clone(),
name,
raw_content: raw,
built_content,
fixed_content,
},
Ok(NavPage {
path: path.clone(),
name,
fm,
))
raw_content: raw,
built_content,
fixed_content,
})
}

pub fn process_page(path: &Path, name: Option<String>) -> Result<NavPage> {
let (page, fm) = process_content(path, name)?;
pub fn process_folder(path: &Path, name: Option<String>) -> Result<NavFolder> {
let index = process_page(&path.join("index.md"), name)?;

ensure!(
fm.is_none(),
"regular (non-index) page {path} cannot have front matter"
!index.fm.nav.is_empty(),
"index page {path} is missing fm nav"
);

Ok(page)
}

pub fn process_index(path: &Path, name: Option<String>) -> Result<NavIndex> {
let (page, fm) = process_content(path, name)?;

let fm = unwrap!(fm, "index page {path} is missing front matter");

Ok(NavIndex { page, fm })
}

pub fn process_folder(path: &Path, name: Option<String>) -> Result<NavFolder> {
let index = process_index(&path.join("index.md"), name)?;

let children = index
.fm
.nav
Expand Down Expand Up @@ -124,7 +108,8 @@ pub fn process_include(dir: &Path, name: String) -> Result<NavCategory> {
bail!("cannot include/* an index file at {path}");
}

children.push(process_page(&path, None)?);
// regular pages can't have nav
children.push(process_page(&path, None).and_then(ensure_page_has_no_nav)?);
}

children.sort_by(|x, y| x.name.cmp(&y.name));
Expand All @@ -143,3 +128,11 @@ pub fn process_category(dir: &Path, name: String, elems: &[NavElem]) -> Result<N

Ok(NavCategory { name, children })
}

fn ensure_page_has_no_nav(page: NavPage) -> Result<NavPage> {
if page.fm.nav.is_empty() {
Ok(page)
} else {
Err(anyhow!("non index page {} cannot have fm nav", page.path))
}
}

0 comments on commit 7d0c9bd

Please sign in to comment.