A common practice when creating markdown files is to use something called frontmatter.
Frontmatter is a section at the beginning of the file that usually gets chopped off to produce metadata about the file before the file is processed as markdown.
The frontmatter is typically written in a format like yaml, like this.
layout: post
tags: []
status: draft
title: A Post
slug: a-post
---
# A Post
Let’s start by defining a set of frontmatter that we want to get from our CLI arguments as a custom struct.
struct Frontmatter {
layout: String,
tags: Vec<String>,
status: String,
title: String,
slug: String,
}
- The
layout
here would be for the post layout when rendering - The
tags
for categories of post - The
status
for the publish status - The
title
is going to be the post’s title - and the
slug
is going to be what we’d use for a URL
Constructing our new struct is relatively simple once we’ve defined it. We write out our struct’s name, followed by the fields in the struct with their values.
In this case, all of our values come from args
.
args.title
needs to be cloned because we still use it later when writing the file out and slug
is going to be set using a new third party crate.
let mut filename = args.output_dir.join(&args.title);
filename.set_extension("md");
let frontmatter = Frontmatter {
layout: args.layout,
tags: args.tags,
status: args.status,
title: args.title.clone(),
slug: slug::slugify(&args.title),
};
slug
is a crate that will slugify and remove whitespace from our post title, allowing us to use it in a URL.
cargo add slug
We can use the fully qualified path instead of bringing slugify
into scope with use
. Calling the slugify
function in the slug
crate only needs a shared reference to the title, so that’s all we give it.
slug::slugify(&args.title)
If we want to use the dbg!
macro on frontmatter
, we have to derive Debug
on our Frontmatter
struct.
dbg!(frontmatter);
#[derive(Debug)]
struct Frontmatter {
layout: String,
tags: Vec<String>,
status: String,
title: String,
slug: String,
}
All together, we have a new instance of the Frontmatter
struct that looks pretty similar to our Args
.
❯ cargo run
Compiling scaffold v0.1.0 (/rust-adventure/scaffold)
warning: fields `layout`, `tags`, `status`, `title`, and `slug` are never read
--> src/main.rs:74:5
|
73 | struct Frontmatter {
| ----------- fields in this struct
74 | layout: String,
| ^^^^^^
75 | tags: Vec<String>,
| ^^^^
76 | status: String,
| ^^^^^^
77 | title: String,
| ^^^^^
78 | slug: String,
| ^^^^
|
= note: `Frontmatter` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
= note: `#[warn(dead_code)]` on by default
warning: `scaffold` (bin "scaffold") generated 1 warning
Finished dev [unoptimized + debuginfo] target(s) in 0.78s
Running `target/debug/scaffold`
[src/main.rs:33] &args = Args {
layout: "post",
tags: [],
title: "A Post",
status: "draft",
output_dir: "content",
}
[src/main.rs:58] frontmatter = Frontmatter {
layout: "post",
tags: [],
status: "draft",
title: "A Post",
slug: "a-post",
}
There are also a couple warnings about unused fields. The Rust compiler is pretty good about telling us when we have code that we aren’t using, whether that’s fields, Results, use
, or something else.
In the next lesson we’ll serialize this frontmatter to yaml.