DEV Community

Cover image for Building a Terminal TODO App in Rust
Tramposo
Tramposo

Posted on

4 1

Building a Terminal TODO App in Rust

Project Overview

The Terminal TODO App is a command-line task manager built with Rust. It utilizes the tui and crossterm crates to create an interactive terminal user interface.

Key Features

  • Dual list management (TODO and DONE lists)
  • Interactive navigation
  • Task editing and deletion
  • Data persistence
  • Vim-inspired keybindings

Technical Breakdown

1. State Management

The app's state is managed through a central App struct:

struct App {
    todos: Vec<String>,
    done: Vec<String>,
    input: String,
    input_mode: InputMode,
    todo_list_state: ListState,
    done_list_state: ListState,
    editing_index: Option<usize>,
}
Enter fullscreen mode Exit fullscreen mode

This structure captures all the necessary data for the application, including the task lists, current input, and UI state.

2. User Input Handling

User input is processed in the main event loop:

fn run_app<B: tui::backend::Backend>(terminal: &mut Terminal<B>, app: &mut App) -> io::Result<()> {
    loop {
        terminal.draw(|f| ui(f, app))?;

        if let Event::Key(key) = event::read()? {
            match app.input_mode {
                InputMode::Normal => match key.code {
                    // Handle normal mode inputs
                },
                InputMode::Editing => match key.code {
                    // Handle editing mode inputs
                },
                InputMode::EditingExisting => match key.code {
                    // Handle existing item editing inputs
                },
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

This pattern allows for different behaviors based on the current input mode.

3. Rendering the UI

The terminal UI is rendered using the tui crate:

fn ui<B: tui::backend::Backend>(f: &mut Frame<B>, app: &App) {
    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .margin(2)
        .constraints([
            Constraint::Length(3),
            Constraint::Min(0),
            Constraint::Length(3),
        ])
        .split(f.size());

    // Render TODO and DONE lists
    let lists = Layout::default()
        .direction(Direction::Horizontal)
        .constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
        .split(chunks[1]);

    let todo_items: Vec<ListItem> = app.todos.iter().map(|i| ListItem::new(i.as_ref())).collect();
    let todo_list = List::new(todo_items)
        .block(Block::default().borders(Borders::ALL).title("Todo"));

    f.render_stateful_widget(todo_list, lists[0], &mut app.todo_list_state);

    // Similar rendering for DONE list...
}
Enter fullscreen mode Exit fullscreen mode

This function demonstrates how to create a layout and render widgets like lists and input fields.

4. Data Persistence

Task persistence is implemented using simple file I/O:

impl App {
    pub fn save_to_file(&self, filename: &str) -> io::Result<()> {
        let mut file = OpenOptions::new()
            .write(true)
            .create(true)
            .truncate(true)
            .open(filename)?;

        writeln!(file, "[TODO]")?;
        for item in &self.todos {
            writeln!(file, "{}", item)?;
        }

        writeln!(file, "[DONE]")?;
        for item in &self.done {
            writeln!(file, "{}", item)?;
        }

        Ok(())
    }

    pub fn load_from_file(filename: &str) -> io::Result<Self> {
        // Implementation of file loading...
    }
}
Enter fullscreen mode Exit fullscreen mode

This approach is straightforward to save and load the application state between sessions.

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

Top comments (0)

The Most Contextual AI Development Assistant

Pieces.app image

Our centralized storage agent works on-device, unifying various developer tools to proactively capture and enrich useful materials, streamline collaboration, and solve complex problems through a contextual understanding of your unique workflow.

👥 Ideal for solo developers, teams, and cross-company projects

Learn more