Skip to main content

Rust CLI ToDo

GitLab Repository

A console based todo app, written in rust, saving to sqlite.

Update .toml dependencies

  1. cargo install cargo-update
  2. cargo install-update -a

Usage

Create

-b body

-d due_date

td create -b "todo content"

td create -b "todo content" -d 2021-02-15

List

td td list

Delete

-i -> row id

td delete -i 1

Code

main

hideous main function - first actual rust program I am writing

main()
fn main() {
let yaml = load_yaml!("cli.yml");
let matches: ArgMatches = App::from(yaml).get_matches();

// setup db
let connection: Connection = api::database::open_connection();
api::database::initial_table_setup(&connection);

// handle arguments
if let Some(matches) = matches.subcommand_matches(COM_CREATE) {
let mut body: &str = "";
let mut date: &str = "";

if matches.is_present(ARG_BODY) {
body = match matches.value_of(ARG_BODY) {
None => "",
Some(x) => {
x.trim()
}
};
}
if matches.is_present(ARG_DATE) {
date = match matches.value_of(ARG_DATE) {
None => "",
Some(arg_date) => {
if util::util::is_date(arg_date.trim()) {
arg_date.trim()
} else {
println!("Input date of wrong format. Needs yyyy-mm-dd.");
""
}
}
};
}
if !body.is_empty() {
let _rows_modified = actions::create::create(&body, &date, &connection);
}
} else if let Some(matches) = matches.subcommand_matches(COM_DELETE) {
let id = match matches.value_of(ARG_ID) {
None => "",
Some(id) => id
};
if !id.is_empty() {
let _rows_modified = actions::delete::delete(id.trim(), &connection);
} else {
println!("Cannot delete for empty id.")
}
} else if let Some(_) = matches.subcommand_matches(COM_LIST) {
actions::list::list(&connection);
}

// default action
actions::list::list(&connection);
// close app
shutdown();
}

API module

Setup

Database Setup
pub fn open_connection() -> Connection {
return Connection::open(NAME_DATABASE).unwrap();
}

pub fn initial_table_setup(connection: &Connection) -> usize {
let rows_modified: usize = match connection.execute(
"create table if not exists todos (
id integer primary key,
body text not null,
due_date date
)",
NO_PARAMS,
) {
Ok(x) => x,
Err(_) => 0,
};

return rows_modified;
}

Create Entry

Create Entry
use rusqlite::Connection;
use crate::constants::{TABLE_COL_BODY, TABLE_COL_DUE_DATE, TABLE_TODOS};

pub fn create(body: &str, date: &str, connection: &Connection) -> usize {
let mut rows_modified: usize = 0;

if !body.is_empty() {
let query: &str = &*format!(
"INSERT INTO {} ({}, {}) values (?1, ?2)",
TABLE_TODOS,
TABLE_COL_BODY,
TABLE_COL_DUE_DATE
);

rows_modified = match connection.execute(
query,
&[body.to_string(), date.to_string()],
) {
Ok(x) => x,
Err(_) => 0,
};
}

return rows_modified;
}

Read Entries

Get all DB Entries
use rusqlite::{Connection, params};

use crate::constants::TABLE_TODOS;

#[derive(Debug)]
struct Todo {
id: i32,
body: String,
date: String,
}

pub fn list(connection: &Connection) {
let query: &str = &*format!(
"SELECT * FROM {}",
TABLE_TODOS,
);
let mut statement = connection.prepare(
query
).expect("Error preparing SELECT statement.");

let todos_iter = statement.query_map(params![], |row| {
Ok(Todo {
id: row.get(0).expect("Error unwrapping id."),
body: row.get(1).expect("Error unwrapping body."),
date: row.get(2).expect("Error unwrapping date."),
})
}).unwrap();

for todo in todos_iter {
let todo_item: Todo = todo.unwrap();
println!("ID: {} -- {} -> ({})", todo_item.id, todo_item.body, todo_item.date);
}
}

Delete Entry

Delete Entry
use rusqlite::Connection;
use crate::constants::{TABLE_COL_ID, TABLE_TODOS};

pub fn delete(id: &str, connection: &Connection) -> usize {
let mut rows_modified: usize = 0;

if !id.is_empty() {
let query: &str = &*format!(
"DELETE FROM {} where {} like (?1)",
TABLE_TODOS,
TABLE_COL_ID
);

rows_modified = match connection.execute(
query,
&[id],
) {
Ok(x) => x,
Err(_) => 0,
};
}

return rows_modified;
}