Not long ago I wrote about dipping my toes into Rust where I discussed what Rust is and why I’m interested to learn it. Now I feel like I need to try my hands on a real project which scratches an itch of mine and at the same time allows me to exercise what I learned.

Setting up a new Rust project

So this step seems easy enough. I’m creating a new repo on GitHub, calling it worldcrab and selecting LGPL2. I’m doing it this way around so that there’s no need to configure the local repo manually which has a main branch.

Now there’s a repo to clone. And let’s get cargo to setup the barebones project.

git clone git@github.com:kalikiana/worldcrab.git
cd worldcrab
cargo init

In fact we can already run this just to confirm that our development setup is working.

cargo run
   Compiling worldcrab v0.1.0 (/home/kalikiana/bau/worldcrab)
    Finished dev [unoptimized + debuginfo] target(s) in 0.74s
     Running `target/debug/worldcrab`
Hello, world

Hack, hack, hack, hack, hack

The template most importantly provides a main.rs which we can modify until we have a somewhat sensible proof of concept. Let’s not worry about modularization for now but try and make this a usable command line tool.

And be sure to add unit tests early. In typical Rust fashion this means adding something like this:

#[cfg(test)]
mod tests {
    // Note this useful idiom: importing names from outer (for mod tests) scope.
    use super::*;

    #[test]
    fn test_add() {
    }
}

You can in fact simply add this to your main.rs. There will be a warning if you’re not actually calling a function that you defined because the super::* imports local functions.

Besides actual Rust code the Cargo.toml will need updating to contain our dependencies, that is the crates required by the code. This could be something like this:

[dependencies]
yaml-rust = "0.4"
chrono = "0.4"
gray_matter = "0.2"
tempfile = "3.2.0"

We’re not discussing any particular non-core crates here so this is a pretty arbitrary list of dependencies. If you happen to need to work with YAML, timestamps, Front Matter and temporary files these might be crates to check out. Who knows? Most importantly if you need a crate, specify your requirements here.

Please integrate this continuously

It should go without saying that, although it’s a proof of concept for now, I’ll add a basic template for CI, in this case GitHub Actions just because it’s the simplest option when the project is hosted on GitHub. Time will tell if it’s going to do the job in the long run. So let’s add a file .github/workflows/test.yaml with the following contents:

name: Run cargo test cases

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
      - uses: icepuma/rust-action@master
        with:
          args: cargo fmt -- --check && cargo clippy && cargo test

This includes formatting, linting and execution of unit tests. If any of those fail, CI will fail. Based on the Rust Action in the Marketplace. This can be customized later down the line but should suffice for the time being.

Let me know if I’m using vulnerable dependencies

Since this is Rust and the upstream pulls in dependencies, checking for known vulnerabilities is a good idea. A handy way to check and even attempt an automatic update of dependencies is via audit-check like so:

cargo install cargo-audit --features=fix
cargo audit fix

Of course having this checked automatically in CI with an issue created for detected issues is what you really want. You can do this easily by adding a file .github/workflows/audit.yaml with the following contents:

name: Security audit
on:
  schedule:
    - cron: '0 0 * * *'
jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - uses: actions-rs/audit-check@v1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

Wrap it up, put it in a box, ship it

In order for people to be able to use it the obvious way would seem to be publishing this on the Rust crate registry. To that end I followed these steps:

  • Login in via GitHub
  • Create a New Token in the account settings
  • Run the command shown once, which then stores it for later use
cargo login ***********************************
       Login token for `crates.io` saved

And, too, Cargo.toml has to be extended with the required fields:

license-file = "LICENSE"
description = "A static meta blog generator aka a planet"
homepage = "https://github.com/kalikiana/worldcrab/"
repository = "https://github.com/kalikiana/worldcrab/"
readme = "README.md"
documentation = "https://docs.rs/worldcrab"

You may also want to put your name:

authors = ["Liv Dywan <liv@twotoasts.de>"]

Obviously the README.md file is one that should be created as well, at least a basic one:

## World Crab

The world is changing. A mouse got lose and fell off the discworld. Consequently the elephants got scared and hopped off Great A'Tuin's back. As luck would have it a gigantic crab with four gophers on its back took its place.

![GitHub Actions](https://github.com/kalikiana/worldcrab/actions/workflows/test.yaml/badge.svg)
![Crates.io](https://img.shields.io/crates/d/worldcrab)

### What's this project about?

A static meta blog generator aka a planet.

Imagine you have a bunch of static blogs made with [Hugo](https://gohugo.io/) and you're looking for a way to aggregate those blogs easily without worrying too much about the details and without duplicating metadata. If this sounds too good to be true, the world crab is for you!

### How do I build and run this?

This project is built using cargo, which means you can simply do this:

```bash
cargo run
```

Now what?

If you haven’t yet, commit and push everything. Push early, push often. It should go without saying that you don’t want to lose more than a day’s worth of work if you can help it. But that’s a topic for another day.

# Add all files
git commit -v
git push origin HEAD

The next steps are to deploy this project somewhere, blog about it and collect some feedback. Good luck!