Skip to main content

oflow/
commands.rs

1// Copyright (c) 2026
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7use crate::models::TodoList;
8use clap::{Parser, Subcommand};
9use sqlx::SqlitePool;
10use std::path::PathBuf;
11
12#[derive(Parser)]
13#[command(author, version, about, long_about = None)]
14pub struct Cli {
15    /// Custom data directory path (defaults to OS-specific data directory)
16    /// This directory should contain todos.db and todos.toml
17    #[arg(short, long, default_value = None)]
18    pub data_dir: Option<String>,
19
20    #[command(subcommand)]
21    pub command: Commands,
22}
23
24#[derive(Subcommand)]
25pub enum Commands {
26    /// Add a new todo item
27    ///
28    /// Example: oflow add "Buy milk"
29    Add { content: String },
30
31    /// Mark a todo as finished
32    ///
33    /// Example: oflow finish "Buy milk"
34    Finish { content: String },
35
36    /// Edit an existing todo's content
37    ///
38    /// Example: oflow edit "Buy milk" "Buy eggs"
39    Edit {
40        /// The content to search for
41        find: String,
42        /// The new content to replace with
43        replace: String,
44    },
45
46    /// Clean all finished/completed todos
47    ///
48    /// Example: oflow clean
49    Clean,
50
51    /// List all the todos
52    ///
53    /// Example: oflow list
54    List,
55
56    /// Run the TUI interface
57    ///
58    /// Example: oflow tui
59    Tui,
60
61    /// Show data file paths
62    ///
63    /// Example: oflow path
64    Path,
65}
66
67pub async fn execute_command(
68    cli: Cli,
69    mut tdlist: TodoList,
70    pool: &SqlitePool,
71    data_dir: &PathBuf,
72) -> color_eyre::Result<TodoList> {
73    match cli.command {
74        Commands::Add { content } => {
75            tdlist.add_todo_db(content, pool).await?;
76        }
77        Commands::Finish { content } => {
78            tdlist.finish_todo_db(content, pool).await?;
79        }
80        Commands::Edit { find, replace } => {
81            tdlist.edit_todo_db(find, replace, pool).await?;
82        }
83        Commands::Clean => {
84            tdlist.clean_todo_db(pool).await?;
85        }
86        Commands::List => {
87            tdlist.list_todos();
88        }
89        Commands::Tui => {
90            tdlist = crate::tui::app::run_tui(tdlist)?;
91        }
92        Commands::Path => {
93            let db_path = data_dir.join("todos.db");
94            let toml_path = data_dir.join("todos.toml");
95            println!("Data directory: {}", data_dir.display());
96            println!("Database:       {}", db_path.display());
97            println!("TOML export:    {}", toml_path.display());
98        }
99    }
100
101    tdlist.sync_to_db(pool).await?;
102    Ok(tdlist)
103}
104
105#[cfg(test)]
106mod args_test {
107    use super::*;
108
109    #[test]
110    fn test_add() {
111        let args = vec!["oflow", "add", "Buy milk"];
112        let cli = Cli::try_parse_from(args).expect("Failed to parse arguments");
113
114        match cli.command {
115            Commands::Add { content } => {
116                assert_eq!(content, "Buy milk");
117            }
118            _ => panic!("Expected Add command"),
119        }
120    }
121
122    #[test]
123    fn test_finish() {
124        let args = vec!["oflow", "finish", "Buy milk"];
125        let cli = Cli::try_parse_from(args).expect("Failed to parse arguments");
126
127        match cli.command {
128            Commands::Finish { content } => {
129                assert_eq!(content, "Buy milk");
130            }
131            _ => panic!("Expect Finish command"),
132        }
133    }
134
135    #[test]
136    fn test_edit() {
137        let args = vec!["oflow", "edit", "Buy milk", "Buy eggs"];
138        let cli = Cli::try_parse_from(args).expect("Failed to parse arguments");
139
140        match cli.command {
141            Commands::Edit { find, replace } => {
142                assert_eq!(find, "Buy milk");
143                assert_eq!(replace, "Buy eggs");
144            }
145            _ => panic!("Expect Edit command"),
146        }
147    }
148}