Building my first rust crate

5 min read

Ever since I started using terminal applications, I have wanted to try building my own. They are less complex than a web app, and don’t require any server to run[1]. In picking a problem to solve, I wanted something that would be beneficial to me, but also something that could realistically help others. Having explored vja as a command line tool for Vikunja, I thought it made sense to make a terminal user interface that performed similar functions. Ratatui seemed like a great library to use, although I was skeptical about having to use rust. With the help of chatgpt, I was able to get to a minimum viable product, and publish the app on crates.io.

Setup

I’ve written about coding with ai before, and have stuck with the same approach since; I use the browser to ask the chatbot questions, and then keep the terminal free of clutter. For this application, it was a little trickier, because I’ve had no experience with rust to date beyond a few Exercism exercises. Surprisingly, the syntax wasn’t the hardest part, rather it was determining the organization of files and where to put different parts of the application. It started out quite basic when just reading tasks from the server, but grew in complexity as more features were added. At the time of publishing, here is the structure I landed on:

Cargo.toml
Cargo.lock
src/
  api.rs
  app.rs
  main.rs
  models.rs
  parser.rs
  ui.rs

Importing things between files is quite similar to python or javascript, you define the things you want to import at the top of the file, and can pull from either crates online or other parts of the application.

Current features

The current feature set is quite basic. The app can:

  1. Read tasks, with a filter to include/exclude completed tasks.
  2. Add tasks with support for a description and priority.

What worked well

Implementing unit tests was a huge help, and something that chatgpt is quite good at. A lot of the development was troubleshooting the structure of the api calls, and separating out whether the issue was due to the parser was very useful. Configuring the application was also quite simple, as it just needs the instance to make the calls to, and the api token to authenticate said requests.

Challenges

A major barrier is that the chatbot would frequently forget previous changes to the file, and would change functionality. For example, priority is parsed via ! followed by a number between 1 and 5. At first chatgpt thought I wanted repeated exclamation marks, then it switched to p: syntax after I started working on due dates with due: parsing. Another difficulty was seeing error messages while within the terminal application, because error messages are printed to stdout (which you cannot see because they are covered up by the app). Finally, chatgpt routinely uses outdated crates and deprecated functions. While not unique to rust, perhaps the speed at which rust crates change exacerbates the problem more than it would in other languages.

Considerations about working in a brand new language

Building an app from scratch with a language that I don’t really know much about involved some thinking about risk and outcomes. There were three principles that I decided upon:

  1. The risks that the app presents should something go wrong should be known. In the current state, the worst that can happen is that the app fails to add a task, or otherwise fails to display tasks from the server. Since all requests are passed through the api, they have to be acceptable requests through that particular route. This helps defines the scope of what can fail. Additionally, the app does not do anything with account security or cryptography, and does not exist online so the cybersecurity risk is greatly reduced.
  2. Developing with a new language is not something that should be done if there are users that are dependent on a service, and have not opted-in to the service. I take downloading and installing the crate to be an acceptance that the software comes with no warranty, as laid out in the license. The app also isn’t going to be used as a dependency for anything else.
  3. The goal of the program should be well defined and limited. In this case, the end state is a tui app that reads, edits, and deletes tasks from an instance that the user defines and has permission to modify (via the api key). That’s it, and when those goals are complete, it will be considered a finished product.

Contributions

Part of my hope is that someone out there may also find it useful and either be able to fork it and build off of it for themselves, or submit PR’s for the features they would like. By publishing it in the mvp state, this opens up that possibility. Of course, I still have a lot of functionality that I want to build into it (due date support is on deck).


  1. vikunja-tui needs a server running vikunja, but in general, many tui’s exist without needing an external server. ↩︎