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
layouthere would be for the post layout when rendering - The
tagsfor categories of post - The
statusfor the publish status - The
titleis going to be the post’s title - and the
slugis 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.