Add todos CLI example for postgres

This commit is contained in:
Jon Pacheco 2020-01-27 10:17:58 +00:00
parent f0c88da152
commit eece922ac3
5 changed files with 160 additions and 1 deletions

View File

@ -3,7 +3,8 @@ members = [
".",
"sqlx-core",
"sqlx-macros",
"examples/realworld-postgres"
"examples/realworld-postgres",
"examples/todos-postgres",
]
[package]

View File

@ -0,0 +1,13 @@
[package]
name = "todos-postgres"
version = "0.1.0"
edition = "2018"
workspace = "../../"
[dependencies]
anyhow = "1.0"
async-std = { version = "1.4.0", features = [ "attributes" ] }
futures = "0.3"
paw = "1.0"
sqlx = { version = "0.2", features = ["postgres"] }
structopt = { version = "0.3", features = ["paw"] }

View File

@ -0,0 +1,29 @@
# TODOs Example
## Usage
Declare the database URL:
```
export DATABASE_URL="postgres://postgres@localhost/todos"
```
Create the database:
```
createdb -U postgres todos
```
Load the database schema:
```
psql -d "$DATABASE_URL" -f ./schema.sql
```
Run:
- Add a todo: `cargo run -- add "todo description"`
- Complete a todo: `cargo run -- done <todo id>`
- List all todos: `cargo run`

View File

@ -0,0 +1,5 @@
CREATE TABLE IF NOT EXISTS todos (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
description TEXT NOT NULL,
done BOOLEAN DEFAULT FALSE
);

View File

@ -0,0 +1,111 @@
use sqlx::PgPool;
use std::env;
use structopt::StructOpt;
#[derive(StructOpt)]
struct Args {
#[structopt(subcommand)]
cmd: Option<Command>,
}
#[derive(StructOpt)]
enum Command {
Add { description: String },
Done { id: i64 },
}
#[async_std::main]
#[paw::main]
async fn main(args: Args) -> anyhow::Result<()> {
let mut pool = PgPool::new(&env::var("DATABASE_URL")?).await?;
match args.cmd {
Some(Command::Add { description }) => {
println!("Adding new todo with description '{}'", &description);
println!(
"Added new todo with id {}",
add_todo(&pool, &description).await.unwrap()
);
}
Some(Command::Done { id }) => {
println!("Marking todo {} as done", id);
if complete_todo(&pool, id).await.unwrap() {
println!("Todo {} is marked as done", id);
} else {
println!("Invalid id {}", id);
}
}
None => {
println!("Printing list of all todos");
list_todos(&mut pool).await.unwrap();
}
}
pool.close().await;
Ok(())
}
async fn add_todo(pool: &PgPool, description: &str) -> Result<i64, sqlx::error::Error> {
let mut tx = pool.begin().await?;
let rec = sqlx::query!(
r#"
INSERT INTO todos ( description )
VALUES ( $1 )
RETURNING id
"#,
description.to_string()
)
.fetch_one(&mut tx)
.await?;
tx.commit().await?;
Ok(rec.id)
}
async fn complete_todo(pool: &PgPool, id: i64) -> Result<bool, sqlx::error::Error> {
let mut tx = pool.begin().await?;
let rows_affected = sqlx::query!(
r#"
UPDATE todos
SET done = TRUE
WHERE id = $1
"#,
id
)
.execute(&mut tx)
.await?;
tx.commit().await?;
Ok(rows_affected > 0)
}
async fn list_todos(pool: &mut PgPool) -> Result<(), sqlx::error::Error> {
let recs = sqlx::query!(
r#"
SELECT id, description, done
FROM todos
ORDER BY id
"#
)
.fetch_all(pool)
.await?;
for rec in recs {
println!(
"- [{}] {}: {}",
match rec.done {
true => "x", // Done
false => " ", // Not Done
},
rec.id,
&rec.description,
);
}
Ok(())
}