use inline_sql::inline_sql;
use clap::CommandFactory;
#[inline_sql]
async fn create_table(client: &tokio_postgres::Client) -> Result<(), tokio_postgres::Error> {
query!(CREATE TABLE pets (
name TEXT PRIMARY KEY,
species TEXT NOT NULL
))
}
#[inline_sql]
async fn get_pets(client: &tokio_postgres::Client) -> Result<Vec<Pet>, tokio_postgres::Error> {
query!(SELECT * FROM pets)
}
#[inline_sql]
async fn get_pet_by_name(client: &tokio_postgres::Client, name: &str) -> Result<Option<Pet>, tokio_postgres::Error> {
query!(SELECT * FROM pets WHERE name = $name)
}
#[inline_sql]
async fn add_pet(client: &tokio_postgres::Client, name: &str, species: &str) -> Result<u64, tokio_postgres::Error> {
query!(INSERT INTO pets (name, species) VALUES ($name, $species))
}
#[derive(pg_mapper::TryFromRow)]
#[derive(Debug)]
struct Pet {
name: String,
species: String,
}
#[derive(clap::Parser)]
struct Options {
#[clap(long, short)]
#[clap(global = true)]
url: Option<String>,
#[clap(subcommand)]
command: Command,
}
#[derive(clap::Subcommand)]
enum Command {
CreateTable,
GetPets,
GetPet {
name: String,
},
AddPet {
name: String,
species: String,
},
}
#[tokio::main]
async fn main() {
if let Err(()) = do_main(clap::Parser::parse()).await {
std::process::exit(1);
}
}
async fn do_main(options: Options) -> Result<(), ()> {
let url = options.url
.ok_or_else(|| {
clap::error::Error::<clap::error::RichFormatter>::raw(
clap::error::ErrorKind::MissingRequiredArgument,
"the following required argument is missing: --url URL\n",
)
.with_cmd(&Options::command())
.print()
.ok();
})?;
let (client, connection) = tokio_postgres::connect(&url, tokio_postgres::NoTls)
.await
.map_err(|e| eprintln!("Failed to connect to {url}: {e}"))?;
let connection = async move {
connection.await.map_err(|e| println!("Error in connection with postgres: {e}"))
};
let work = async move {
match options.command {
Command::CreateTable => {
create_table(&client)
.await
.map_err(|e| eprintln!("Failed to create table: {e}"))?;
Ok(())
},
Command::GetPets => {
let pets = get_pets(&client)
.await
.map_err(|e| eprintln!("Failed to get pets: {e}"))?;
for pet in &pets {
println!("Name: {}, species: {}", pet.name, pet.species);
}
println!("Total: {}", pets.len());
Ok(())
},
Command::GetPet { name } => {
let pet = get_pet_by_name(&client, &name)
.await
.map_err(|e| eprintln!("Failed to get pet: {e}"))?
.ok_or_else(|| eprintln!("No pet found with name: {name:?}" ))?;
println!("{pet:#?}");
Ok(())
},
Command::AddPet { name, species } => {
let count = add_pet(&client, &name, &species)
.await
.map_err(|e| eprintln!("Failed to insert pet: {e}"))?;
println!("Inserted {count} rows" );
Ok(())
},
}
};
tokio::try_join!(connection, work)?;
Ok(())
}