• home

  • blogs

    æ
  • art

  • games

  • projects

● tickets

Author(s): Renato Sanchez

Published on Sat Aug 08 2026

Summary: task phase tracking CLI tool, written in Rust.

URL: https://github.com/renatosanz/tickets

#Rust

#CLI

#SQLx


A small, local, single-binary SQLite ticket manager for the terminal.

tickets preview

  • Version: 0.1.0
  • License: MIT
  • Language: Rust, edition 2024
  • Build & stack: Cargo + tokio 1.53.1 + sqlx 0.9.0 (SQLite)

What is it?

tickets is a command-line ticket manager. A ticket is a title, a description, a status, and a creation timestamp. Five simple actions, add, delete, list, setstatus and getdetail, all that I need for tracking the issues around my projects. Nothing more complex, this is for me, if it works for you great, if it is not, you’re free to modify it.

Why?

I started this to learn Rust properly, not to ship something useful. About a year ago I tried to take on a larger project without really understanding the language first, so I went back and rebuilt the fundamentals on purpose: ownership and borrowing, generics, enums and pattern matching.

Some key points

1. Content-hash ids -> src/models/ticket.rs

Ids are SipHash digests, not sequence numbers. Ticket::new formats the title, the description, and a UTC timestamp into one string, hashes it with DefaultHasher, and truncates the result to 32 bits with a bitmask. The cost is arithmetic rather than bookkeeping: 2^32 possible ids puts collision odds near 50% at roughly 77,000 tickets (future updates will decrease this probability), and ids are rendered as 8 hex characters.

2. Two-pass argument parsing with no parser crate -> src/main.rs

Since any command-line interface (CLI) tool involves additional parameters—such as general usage, help, or verbose mode—I implemented a two-level check for input parameters: first, it determines whether logging should be set to debug level (-v) or error level (the default); next, it checks if --help has been invoked (in which case the help message, including examples and usage information, is displayed). Following these validations, the execution function is called, provided the parameters are correct and correspond to a valid tool function; otherwise, an error is raised. Finally, the action is executed and a result is obtained; if any issue arises, the cause is displayed.

Architecture Map

  • src/main.rs — process entry: argument ingestion, logger level selection, the help short-circuit
  • src/errors.rs — the single error type; every Display arm is a user-facing message, and From<ParseIntError> (src/errors.rs:68-76) is what lets ? cross into utils/validation.rs:44
  • src/utils/constants.rs — the entire help text as one constant
  • src/models/ticket.rs — persistence and rendering: the six SQL statements at src/models/ticket.rs:75, :166, :192, :211, :227, :250, and both view formatters
  • src/models/mod.rs — module root
  • src/state.rs — the Action enum, the resolved State, and the single place a database connection is opened
  • src/utils/validation.rs — the only place raw strings become typed values
  • src/utils/mod.rs — module root

The module graph is not strictly layered: src/utils/validation.rs:6 imports Action from state, while src/state.rs:3 imports the same validation module back, so those two reference each other.

Build pipeline: .rs sources → rustc via Cargo → a single native binary at target/release/tickets.

back to the top