diff --git a/Cargo.toml b/Cargo.toml index 300f64431..b936725c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,8 @@ members = [ ".", "sqlx-core", "sqlx-macros", - "examples/realworld-postgres" + "examples/realworld-postgres", + "examples/todos-postgres", ] [package] diff --git a/examples/todos-postgres/Cargo.toml b/examples/todos-postgres/Cargo.toml new file mode 100644 index 000000000..94dc19007 --- /dev/null +++ b/examples/todos-postgres/Cargo.toml @@ -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"] } diff --git a/examples/todos-postgres/README.md b/examples/todos-postgres/README.md new file mode 100644 index 000000000..12e667be4 --- /dev/null +++ b/examples/todos-postgres/README.md @@ -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 ` +- List all todos: `cargo run` + + diff --git a/examples/todos-postgres/schema.sql b/examples/todos-postgres/schema.sql new file mode 100644 index 000000000..0ae99a33f --- /dev/null +++ b/examples/todos-postgres/schema.sql @@ -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 +); diff --git a/examples/todos-postgres/src/main.rs b/examples/todos-postgres/src/main.rs new file mode 100644 index 000000000..43859ba55 --- /dev/null +++ b/examples/todos-postgres/src/main.rs @@ -0,0 +1,111 @@ +use sqlx::PgPool; +use std::env; +use structopt::StructOpt; + +#[derive(StructOpt)] +struct Args { + #[structopt(subcommand)] + cmd: Option, +} + +#[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 { + 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 { + 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(()) +}