How to Create a Static Site Generator using Rust in less than 100 LOC

njugunamureithi

Njuguna Mureithi

Posted on August 5, 2023

How to Create a Static Site Generator using Rust in less than 100 LOC

In this tutorial, we will learn how to create a static site generator using Rust. We will use the following tools:

  • Rust (of course)
  • Markdown (Our content will be in markdown)
  • FrontMatter (Markdown content will contain YAML metadata)

Let's get started!

cargo new samosa-site
Enter fullscreen mode Exit fullscreen mode

Create a new folder named pages inside samosa-site and move the following files into it:

  • index.md (The root of our site)
  • recipes/samosa-waru.md
  • recipes/samosa-beef.md

In the future, if we add more recipes, we expect the code to keep working.

Let's import some libraries in our Cargo.toml:

hirola = { version ="0.3", default-features = false, features = ["ssr"] } # For rendering html to string
glob = "0.3.1" # For searching through directories
comrak = { version = "0.18" } # For parsing markdown
fronma = "0.2" # For parsing front matter
serde = { version = "1", features = ["derive"] } # Required by fronma
Enter fullscreen mode Exit fullscreen mode

Now, let's start writing some code:

Define Front Matter Content

#[derive(Debug, Deserialize)]
struct Seo {
    title: String
    //....
}
Enter fullscreen mode Exit fullscreen mode

This would be read from markdown looking like this:

---
title: "A recipe for Beef samosas cooked Kenyan style"
---
# Kenyan style Beef samosas recipe
.....(more content here)
Enter fullscreen mode Exit fullscreen mode

That done, let's now handle the layout for our site.
Here is a basic example but you should be able to write complex layouts

fn layout(seo: Seo) -> Dom {
    html! {
        <html>
            <head>
            <title>{&seo.title} " | Awesome Samosa Site"</title>
            <meta charset="utf-8"/>
            <meta property="og:title" content={&seo.title}/>
            </head>
            <body>
            // markdown content will go here
            "__MARKDOWN_CONTENT_HERE__"
            <body>
        </html>
    }
}
Enter fullscreen mode Exit fullscreen mode

Reading Markdown Files

We will open a file, parse it, and return the HTML and Seo content in a tuple.

fn read_page(path: &PathBuf) -> (String, Seo) {
    use comrak::{markdown_to_html, ComrakOptions};
    let markdown = std::fs::read_to_string(path).unwrap(); // Open the path
    let data = fronma::parser::parse::<Seo>(&markdown)
        .expect(&format!("in file: {}", path.to_string_lossy())); // Parse front matter and markdown
    let res = markdown_to_html(&data.body, &ComrakOptions::default()); // convert markdown to html
    (res, data.headers)
}
Enter fullscreen mode Exit fullscreen mode

Bringing it all together

fn main() {
    use glob::glob;
    for entry in glob("src/pages/**/*.md").expect("Failed to read glob pattern") {
        match entry {
            Ok(path) => {
                let (content, seo) = read_page(&path);
                let mut layout = "<!DOCTYPE html>".to_string();
                layout.extend(render_to_string(layout(seo)).chars());
                let html_path = path
                    .to_string_lossy()
                    .replace("src/pages", "dist")
                    .replace(".md", ".html");
                std::fs::create_dir_all("dist/recipes").unwrap();
                let file = File::create(&html_path).unwrap();
                let html_page = layout.replace("__MARKDOWN_CONTENT_HERE__", &content).as_bytes();
                file.write_all(html_page).expect("Unable to write data");

            }
            Err(e) => println!("{:?}", e),
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

That's it! Now, run cargo run --release and check the dist folder for your generated static site.

cd dist
python3 -m http.server
Enter fullscreen mode Exit fullscreen mode

What next?

  • You can add a layout option in our Seo and frontmatter then use that to render different layouts
enum Layout {
    BlogPost,
    Recipe,
    ...
}
struct SeoFroma {
    title: String,
    layout: Layout
}
Enter fullscreen mode Exit fullscreen mode
  • You can learn more about comrak and learn about plugins such as code highlighters.
  • You can learn more about hirola and learn how to write reactive UIs in Rust.
💖 💪 🙅 🚩
njugunamureithi
Njuguna Mureithi

Posted on August 5, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related