flatpage 2.0.0

A simple filesystem-based Markdown page loader
Documentation
#![cfg_attr(docsrs, feature(doc_cfg))]
/*!
# flatpage

A simple filesystem-based Markdown page loader.

## Reading a page

Reading a page with an optional [`DefaultFrontmatter`] is as simple as:

```rust
let root_folder = "./pages";
if let Some(home) = flatpage::by_url(root_folder, "/").unwrap() {
    println!("frontmatter: {:?}", home.frontmatter);
    println!("markdown body: {}", home.body());
    println!("html body: {}", home.html());
} else {
    println!("No home page");
}
```

## Custom frontmatter

You can define your own frontmatter type when you need a custom schema. Any
type that implements [`serde::Deserialize`] can be parsed.

```rust
#[derive(Debug, serde::Deserialize)]
struct Frontmatter {
    slug: String,
}

if let Some(page) = flatpage::FlatPage::<Frontmatter>::by_url("./pages", "/").unwrap() {
    println!("slug: {}", page.frontmatter.slug);
}
```

## Page title

To extract title from pages with different frontmatter structure or without frontmatter at all,
implement [`TitleFrontmatter`] trait. It makes available [`FlatPage::title`] which looks into
[`TitleFrontmatter::title`] first and then parse the title from the markdown body.

```rust
#[derive(Debug, serde::Deserialize)]
struct Frontmatter {
    title: String,
}

impl flatpage::TitleFrontmatter for Frontmatter {
    fn title(&self) -> Option<&str> {
        Some(&self.title)
    }
}

if let Some(page) = flatpage::FlatPage::<Frontmatter>::by_url("./pages", "/").unwrap() {
    println!("title: {}", page.title());
}
```

## Folder structure

The only characters allowed in URL segments are ASCII letters, numbers, hyphens,
underscores, and dots. URLs map to nested Markdown files, and `index.md` is used
for `/` and folder index pages. Trailing slashes are significant, so `/foo` and
`/foo/` map to different files. Empty path segments plus `.` and `..` are
rejected.

| Url        | File name      |
| ---------- | -------------- |
| `/`        | `index.md`     |
| `/foo`     | `foo.md`       |
| `/foo/`    | `foo/index.md` |
| `/foo/bar` | `foo/bar.md`   |

## Features

- `yaml`: enable YAML frontmatter support
- `toml`: enable TOML frontmatter support
- `json`: enable JSON frontmatter support
- `full`: enable all formats (`json`, `toml`, `yaml`) - enabled by default
*/

use std::path::Path;

#[cfg(not(any(feature = "json", feature = "toml", feature = "yaml")))]
compile_error!("enable at least one frontmatter feature: json, toml, yaml");

mod error;
mod frontmatter;
mod markdown;
mod page;
#[cfg(test)]
mod test_helpers;
pub(crate) mod util;

pub use error::{Error, Result};
pub use frontmatter::{DefaultFrontmatter, TitleFrontmatter};
pub use page::FlatPage;

/// Returns a page with the default frontmatter schema by URL.
pub fn by_url(root: impl AsRef<Path>, url: &str) -> Result<Option<FlatPage<DefaultFrontmatter>>> {
    FlatPage::<DefaultFrontmatter>::by_url(root, url)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_helpers::{TestDir, write_page};

    #[test]
    fn by_url_reads_default_frontmatter_pages() {
        let root = TestDir::new();
        write_page(root.path(), "index.md", "# Welcome");

        let page = by_url(root.path(), "/").unwrap().unwrap();
        assert_eq!(page.title(), "Welcome");
        assert_eq!(page.frontmatter.description, None);
    }
}