# Frontmatter Gen (frontmatter-gen)
<img src="https://kura.pro/frontmatter-gen/images/logos/frontmatter-gen.svg"
alt="FrontMatter Gen logo" height="66" align="right" />
A high-performance Rust library for parsing and serialising frontmatter in YAML, TOML, and JSON formats. Built for safety, efficiency, and ease of use.
<center>
[![Made With Love][made-with-rust]][08] [![Crates.io][crates-badge]][03] [![lib.rs][libs-badge]][01] [![Docs.rs][docs-badge]][04] [![Codecov][codecov-badge]][06] [![Build Status][build-badge]][07] [![GitHub][github-badge]][02]
• [Website][00] • [Documentation][04] • [Report Bug][02] • [Request Feature][02] • [Contributing Guidelines][05]
</center>
## Overview 🚀
`frontmatter-gen` is a comprehensive Rust library that provides robust handling of frontmatter in content files. It delivers a type-safe, efficient solution for extracting, parsing, and serialising frontmatter in multiple formats. Whether you're building a static site generator, content management system, or any application requiring structured metadata, `frontmatter-gen` offers the tools you need.
## Key Features 🎯
- **Zero-copy parsing** for optimal memory efficiency
- **Multi-format support** (YAML, TOML, JSON)
- **Type-safe operations** with comprehensive error handling
- **Secure processing** with input validation and size limits
- **Async support** with the `ssg` feature flag
- **Command-line interface** for direct manipulation
## Available Features 🛠️
This crate provides several feature flags to customise its functionality:
- **default**: Core frontmatter parsing functionality only
- **cli**: Command-line interface tools for quick operations
- **ssg**: Static Site Generator functionality (includes CLI features)
Configure features in your `Cargo.toml`:
```toml
[dependencies]
# Enable CLI support for validation and extraction
frontmatter-gen = { version = "0.0.4", features = ["cli"] }
# Enable all features (validation, extraction and static site generation)
frontmatter-gen = { version = "0.0.4", features = ["ssg"] }
```
Installation via cargo:
```bash
# Install with CLI support
cargo install frontmatter-gen --features="cli"
# Install with SSG support
cargo install frontmatter-gen --features="ssg"
```
## Getting Started 📦
### Library Usage
Add this to your `Cargo.toml`:
```toml
[dependencies]
# Core library with command-line interface and SSG support
frontmatter-gen = { version = "0.0.4", features = ["cli", "ssg"] }
```
## Basic Usage 🔨
### Extract and Parse Frontmatter
```rust
use frontmatter_gen::extract;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let content = r#"---
title: My Document
date: 2025-09-09
tags:
- documentation
- rust
---
# Content begins here"#;
let (frontmatter, content) = extract(content)?;
if let Some(title) = frontmatter.get("title").and_then(|v| v.as_str()) {
println!("Title: {}", title);
}
println!("Content: {}", content);
Ok(())
}
```
### Format Conversion
```rust
use frontmatter_gen::{Frontmatter, Format, Value, to_format};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut frontmatter = Frontmatter::new();
frontmatter.insert("title".to_string(), Value::String("My Document".to_string()));
let json = to_format(&frontmatter, Format::Json)?;
println!("JSON output: {}", json);
assert!(json.contains(r#""title":"My Document""#));
Ok(())
}
```
## CLI Tool 🛠️
The `fmg` command provides comprehensive frontmatter operations:
```bash
# Extract frontmatter in various formats
fmg extract input.md --format yaml
fmg extract input.md --format toml
fmg extract input.md --format json
# Save extracted frontmatter
fmg extract input.md --format yaml --output frontmatter.yaml
# Validate frontmatter
fmg validate input.md --required title,date,author
```
You can also use the CLI directly from the source code:
```bash
# Extract frontmatter in various formats
cargo run --features="cli" extract input.md --format yaml
cargo run --features="cli" extract input.md --format toml
cargo run --features="cli" extract input.md --format json
# Save extracted frontmatter
cargo run --features="cli" extract input.md --format yaml --output frontmatter.yaml
```
## Static Site Generation 🌐
Build and serve your static site:
```bash
# Generate a static site with the fmg CLI
fmg build \
--content-dir content \
--output-dir public \
--template-dir templates
or from the source code:
# Generate a static site using cargo
cargo run --features="ssg" -- build \
--content-dir content \
--output-dir public \
--template-dir templates
```
### Serve locally (using Python for demonstration)
```bash
# Change to the output directory
cd public
# Serve the site
python -m http.server 8000 --bind 127.0.0.1
```
Then visit `http://127.0.0.1:8000` in your favourite browser.
## Error Handling 🚨
The library provides comprehensive error handling:
```rust
use frontmatter_gen::{extract, error::Error};
fn process_content(content: &str) -> Result<(), Error> {
let (frontmatter, _) = extract(content)?;
// Validate required fields
for field in ["title", "date", "author"].iter() {
if !frontmatter.contains_key(*field) {
return Err(Error::ValidationError(
format!("Missing required field: {}", field)
));
}
}
Ok(())
}
```
## Logging Support 📝
When the `logging` feature is enabled, the library integrates with Rust's `log` crate for detailed debug output. You can use any compatible logger implementation (e.g., `env_logger`, `simple_logger`).
### Basic Logging Setup
```rust
use frontmatter_gen::extract;
use log::{debug, info, Level, Metadata, Record, set_logger, set_max_level};
struct SimpleLogger;
impl log::Log for SimpleLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Debug
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
println!(
"{} [{}] - {}",
record.target(),
record.level(),
record.args()
);
}
}
fn flush(&self) {}
}
static LOGGER: SimpleLogger = SimpleLogger;
fn init_logger() {
// Explicitly handle logger initialization error
if let Err(e) = set_logger(&LOGGER).map(|()| set_max_level(Level::Debug.to_level_filter())) {
eprintln!("Failed to initialize logger: {}", e);
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize the custom logger
init_logger();
info!("Starting frontmatter extraction");
let content = r#"---
title: My Document
date: 2025-09-09
---
# Content"#;
// Extract frontmatter and remaining content
let (frontmatter, content) = extract(content)?;
debug!("Extracted frontmatter: {:?}", frontmatter);
debug!("Remaining content: {:?}", content);
Ok(())
}
```
#### Advanced Logging Configuration
For more control over logging:
```rust
use frontmatter_gen::{parser, Format};
use log::{debug, info, Level, Metadata, Record, set_logger, set_max_level};
struct SimpleLogger;
impl log::Log for SimpleLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Debug
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
println!("[{}] - {}", record.level(), record.args());
}
}
fn flush(&self) {}
}
static LOGGER: SimpleLogger = SimpleLogger;
fn init_logger() {
set_logger(&LOGGER).expect("Failed to set logger");
set_max_level(Level::Debug.to_level_filter());
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize the custom logger
init_logger();
info!("Starting frontmatter processing");
let yaml = r#"title: Test Document"#;
let frontmatter = parser::parse(yaml, Format::Yaml)?;
debug!("Parsed frontmatter: {:?}", frontmatter);
Ok(())
}
```
## CLI Logging 📝
When using the CLI with logging enabled:
```bash
# Set log level via environment variable
RUST_LOG=debug frontmatter_gen extract input.md --format yaml
# Or for more specific control
RUST_LOG=frontmatter_gen=debug,cli=info frontmatter_gen validate input.md
```
The library provides detailed error handling with context:
```rust
use frontmatter_gen::{extract, error::Error};
fn process_content(content: &str) -> Result<(), Error> {
// Extract frontmatter and content
let (frontmatter, _) = extract(content)?;
// Validate required fields
for field in ["title", "date", "author"].iter() {
if !frontmatter.contains_key(*field) {
return Err(Error::ValidationError(
format!("Missing required field: {}", field)
));
}
}
// Validate field types
if let Some(date) = frontmatter.get("date") {
if !date.is_string() {
return Err(Error::ValidationError(
"Date field must be a string".to_string()
));
}
}
Ok(())
}
```
## Documentation 📚
For comprehensive API documentation and examples, visit:
- [API Documentation on docs.rs][04]
- [User Guide and Tutorials][00]
- [Example Code Repository][02]
## Contributing 🤝
We welcome contributions! Please see our [Contributing Guidelines][05] for details on:
- Code of Conduct
- Development Process
- Submitting Pull Requests
- Reporting Issues
## Licence 📝
This project is dual-licensed under either:
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
- MIT licence ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
at your option.
## Acknowledgements 🙏
Special thanks to all contributors and the Rust community for their invaluable support and feedback.
[00]: https://frontmatter-gen.com
[01]: https://lib.rs/crates/frontmatter-gen
[02]: https://github.com/sebastienrousseau/frontmatter-gen/issues
[03]: https://crates.io/crates/frontmatter-gen
[04]: https://docs.rs/frontmatter-gen
[05]: https://github.com/sebastienrousseau/frontmatter-gen/blob/main/CONTRIBUTING.md
[06]: https://codecov.io/gh/sebastienrousseau/frontmatter-gen
[07]: https://github.com/sebastienrousseau/frontmatter-gen/actions?query=branch%3Amain
[08]: https://www.rust-lang.org/
[build-badge]: https://img.shields.io/github/actions/workflow/status/sebastienrousseau/frontmatter-gen/release.yml?branch=main&style=for-the-badge&logo=github
[codecov-badge]: https://img.shields.io/codecov/c/github/sebastienrousseau/frontmatter-gen?style=for-the-badge&token=Q9KJ6XXL67&logo=codecov
[crates-badge]: https://img.shields.io/crates/v/frontmatter-gen.svg?style=for-the-badge&color=fc8d62&logo=rust
[docs-badge]: https://img.shields.io/badge/docs.rs-frontmatter--gen-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
[github-badge]: https://img.shields.io/badge/github-sebastienrousseau/frontmatter--gen-8da0cb?style=for-the-badge&labelColor=555555&logo=github
[libs-badge]: https://img.shields.io/badge/lib.rs-v0.0.4-orange.svg?style=for-the-badge
[made-with-rust]: https://img.shields.io/badge/rust-f04041?style=for-the-badge&labelColor=c0282d&logo=rust