Cronenberg


time

In my previous post I wrote about the plan of how I am going to learn the Rust language. One of the the plans’s points was to write a couple of simple libraries.

So here comes the first library that I wrote in Rust - Cronenberg.

Background

A couple of weeks ago I finally found some time to dive into emacs’s org-mode and I found it very useful. For those who don’t know what org-mode is, it is a mode for keeping notes, maintaining TODO lists, and doing project planning with a fast and effective plain-text system.

I personally use it for planning my day, keeping track of my work tasks and gym programs. I synchronize org-mode files with my Android phone using open source Orgzly app (by the way, there are a couple of apps for ios too).

Org-mode has an amazing deadlines and scheduling feature. I got an idea to write a simple Rust app that will notify me about scheduled events in my org-mode files. The project has a working title of ‘Doomsday’. I use macOS at work and a linux distro at home so I decided to use cron for scheduling system notifications.

I encountered the first problem when I was writing interaction with crontab files: rust ecosystem has a library for parsing cron command entries but it uses nightly version of Rust and an older version of nom (parser combinator framework). I decided to write a simple cron parser myself.

Parsing cron command entries

cronenberg provides two core components

  • TimeItem: An enum that represents cron command time or date field
pub enum TimeItem {
    AllValues,
    SingleValue(u8),
    MultipleValues(Vec<u8>),
    Interval((u8, u8)),
}
  • CronItem: A struct that represents cron command entry, for example, * * 5-7 1,2,5 8 sudo rm -rf /
pub struct CronItem {
    pub minute: TimeItem,
    pub hour: TimeItem,
    pub day_of_month: TimeItem,
    pub month: TimeItem,
    pub day_of_week: TimeItem,
    pub command: String,
}

Usage example


let s = "* * 5-7 1,2,5 8 sudo rm -rf /";
assert_eq!(
    CronItem::from_str(s).unwrap(),
    CronItem {
        minute: AllValues,
        hour: AllValues,
        day_of_month: Interval((5, 7)),
        month: MultipleValues(vec![1, 2, 5]),
        day_of_week: SingleValue(8),
        command: String::from("sudo rm -rf /"),
    }
);

let cron_item = CronItem {
    minute: MultipleValues(vec![1, 10]),
    hour: Interval((1, 4)),
    day_of_month: Interval((1, 11)),
    month: MultipleValues(vec![1, 2, 5]),
    day_of_week: AllValues,
    command: String::from("sudo rm -rf /"),
};
assert_eq!("1,10 1-4 1-11 1,2,5 * sudo rm -rf /", cron_item.to_string());

I used nom library for parsing. Although I heard good things about this crate, I’m not particularly fond of it. It extensively uses macros and learning this library was like learning a new programming language.

Here’s example of parsing cron time item:

named!(command<&str, &str>,
       do_parse!(
           com: take_until!(COMMAND_TERMINATOR) >>
           tag!(COMMAND_TERMINATOR)             >>
           (com)
       )
);

Roadmap

I’m planning to add more features to cronenberg in the future:

  • words for days of the week: Sun,...,Mon
  • words for months: Jan,..,Dec
  • steps: */5

The library has a GitHub repository - cronenberg.

cronenberg_quote