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, tokio_postgres::Error> { query!(SELECT * FROM pets) } #[inline_sql] async fn get_pet_by_name(client: &tokio_postgres::Client, name: &str) -> Result, 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 { 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, #[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::::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(()) }