Pomodoro on the command line

Monday, April 24 2023

Pomodoro time 🍅

I use pomodoros for work where I’m quite sure I won’t hit flow. Projects past the edge of my comfort zone, or work I’m not that interested in. It’s a great way to keep up a consistent work pace and stop fading into ennui.

Doing a pomodoro is super simple:

  1. You work for 25 minutes,
  2. You break for 5 minutes,
  3. Rinse and repeat, with longer breaks if you like.

So simple, that I thought, what a great first project to learn Rust!

Join the tiny app life

To use the tiny app you can either:

  1. Clone the Github repository (repo) and with Rust’s package manager Cargo installed you can run with ‘cargo run’.
  2. Download this zip which contains a binary (😱). You can invoke the binary as is with ‘pomodoro’.1

So on a command line window run ‘pomodoro’.

This starts a 25-minute timer. After the timer’s over, you’ll hear a beep courtesy of the love of my life. 2

To start the break timer, open a new command line window and acknowledge (ack) the timer by invoking the app with ‘pomodoro ack’.

Once the break is over, you need to ack the end of the break, and it all starts over again. Each pomodoro you do is written to a file ‘~/.pomodoro-stats

Getting schooled

My first thought was ‘just do pomodoros in the background asynchronously’ and off I was tapping away. But what did I actually want to do and what does that sentence even mean?3

Quickly, I realised I’d made the classic mistake of being excited by an idea and not spelling out what I wanted to do. Taking a break and writing a tiny specification helped. I wanted to run a timer ‘in the background’ so I could still use the terminal for other things, like writing posts here.

Running a program in the background sounds deceptively trivial, but just launching an asynchronous program doesn’t let you use the terminal as normal. The easiest way to do this would be to launch the program by putting ‘&' on the end such as ‘pomodoro &’ and letting the OS manage it. But that would open a whole other kettle of fish as you wouldn’t be able to interact with the program directly, and to kill it you’d have to know its process ID. In the end, I let go of the ‘run in the background’ idea, and now the program occupies the terminal window, so you have to use another window for other things, but at least it’s obvious now.

I still had the issue of starting a program with a pomodoro timer and communicating with it from another process that the timer has been ack’d. After talking this through with the super knowledgeable Tom Lockie, he suggested starting a server as part of the program that would listen in on a socket. Enter ‘UNIX domain sockets’ which have the same application interface as a network socket but are really just a file you’re writing to and reading from, and with none of the TCP-faff. What a cool idea. There are a ton of other ways to communicate between programs locally but may be even better 4.

Woah Rust is cool!

There are so many reasons people (‘cRUSTaceans’ 🦀) love Rust. I’m interested in Rust because:

  1. I heard it has no garbage collector, but is memory safe. That’s so cool! (and what does it mean?)
  2. Less and less Python code these days is written in Python. All the cool, new, optimised code seems to be written in C or Rust like polars.
  3. It’s easy to pick up and go. Installing Rust and running a hello world is wonderfully trivial compared to C or C++.

Exercises for the reader

  1. Add your own personalised beeps :)
  2. Add long breaks every 4 pomodoros
  3. What’s up with the 1-2s delay after receiving an ack? Is that just these thread sleeps?

  1. To run the app first time you’ll have to bypass Mac security and open the binary location in Finder and right click ‘open’. Cargo is fast and easy to install, I recommend it over the binary. ↩︎

  2. Can’t be asked to wait 25 minutes to play with this app? Modify the pomodoro timer to ‘::seconds(10)’ as well as the break timer and run it locally with ‘cargo run’. ↩︎

  3. And why would I choose my first Rust program to be asynchronous? Am I a masochist? ↩︎

  4. Pipes, message queues, shared memory, semaphores, memory-mapped files to name some. ↩︎