# TODOs Example
## Setup
1. Declare the database URL
```
export DATABASE_URL="postgres://postgres:password@localhost/todos"
```
2. Create the database.
```
$ sqlx db create
```
3. Run sql migrations
```
$ sqlx migrate run
```
## Usage
Add a todo
```
cargo run -- add "todo description"
```
Complete a todo.
```
cargo run -- done <todo id>
```
List all todos
```
cargo run
```
[package]
name = "todos"
version = "0.1.0"
edition = "2018"
[dependencies]
anyhow = "1.0.58"
sqlx = { version = "0.8.6", features = ["postgres", "runtime-tokio", "tls-native-tls"] }
clap = { version = "4.4.7", features = ["derive"] }
tokio = { version = "1.25.0", features = ["rt", "macros"]}
dotenvy = "0.15.7"
use clap::{Parser, Subcommand};
use sqlx::postgres::PgPool;
use std::env;
#[derive(Parser)]
struct Args {
#[command(subcommand)]
cmd: Option<Command>,
}
#[derive(Subcommand)]
enum Command {
Add { description: String },
Done { id: i64 },
}
#[tokio::main(flavor = "current_thread")]
async fn main() -> anyhow::Result<()> {
let args = Args::parse();
let pool = PgPool::connect(&env::var("DATABASE_URL")?).await?;
match args.cmd {
Some(Command::Add { description }) => {
println!("Adding new todo with description '{description}'");
let todo_id = add_todo(&pool, description).await?;
println!("Added new todo with id {todo_id}");
}
Some(Command::Done { id }) => {
println!("Marking todo {id} as done");
if complete_todo(&pool, id).await? {
println!("Todo {id} is marked as done");
} else {
println!("Invalid id {id}");
}
}
None => {
println!("Printing list of all todos");
list_todos(&pool).await?;
}
}
Ok(())
}
async fn add_todo(pool: &PgPool, description: String) -> anyhow::Result<i64> {
let rec = sqlx::query!(
r#"
INSERT INTO todos ( description )
VALUES ( $1 )
RETURNING id
"#,
description
)
.fetch_one(pool)
.await?;
Ok(rec.id)
}
async fn complete_todo(pool: &PgPool, id: i64) -> anyhow::Result<bool> {
let rows_affected = sqlx::query!(
r#"
UPDATE todos
SET done = TRUE
WHERE id = $1
"#,
id
)
.execute(pool)
.await?
.rows_affected();
Ok(rows_affected > 0)
}
async fn list_todos(pool: &PgPool) -> anyhow::Result<()> {
let recs = sqlx::query!(
r#"
SELECT id, description, done
FROM todos
ORDER BY id
"#
)
.fetch_all(pool)
.await?;
for rec in recs {
println!(
"- [{}] {}: {}",
if rec.done { "x" } else { " " },
rec.id,
&rec.description,
);
}
Ok(())
}
CREATE TABLE IF NOT EXISTS todos
(
id BIGSERIAL PRIMARY KEY,
description TEXT NOT NULL,
done BOOLEAN NOT NULL DEFAULT FALSE
);