Wrapping with a tool

We've prepared all queries, so now we can join them in a binary tool with a command-line interface. In the following code, we will parse some parameters with the clap crate, and run functions to manage users with an established Connection.

Our tool will support three commands. Declare their names as constants:

const CMD_CREATE: &str = "create";
const CMD_ADD: &str = "add";
const CMD_LIST: &str = "list";

Now, we can create the main function using the clap crate to parse our command-line arguments:

fn main() -> Result<(), Error> {

let matches = App::new(crate_name!())
.version(crate_version!())
.author(crate_authors!())
.about(crate_description!())
.setting(AppSettings::SubcommandRequired)
.arg(
Arg::with_name("database")
.short("d")
.long("db")
.value_name("ADDR")
.help("Sets an address of db connection")
.takes_value(true),
)
.subcommand(SubCommand::with_name(CMD_CREATE).about("create users table"))
.subcommand(SubCommand::with_name(CMD_ADD).about("add user to the table")
.arg(Arg::with_name("NAME")
.help("Sets the name of a user")
.required(true)
.index(1))
.arg(Arg::with_name("EMAIL")
.help("Sets the email of a user")
.required(true)
.index(2)))
.subcommand(SubCommand::with_name(CMD_LIST).about("print list of users"))
.get_matches();
// Add connection here
}

The main function returns postgres::Error in case of failure, because all operations we will do relate to our Postgres database connection. We create a clap::App  instance here, and add a --database argument to let users change the address of the connection. We also added three subcommands, create, add, and list, along with extra arguments to the add command that requires the name and email address of a user so that we can insert this into a database.

To create a Connection instance, we use a database argument to extract a connection URL provided by a user with the --db command-line argument, and if it isn't provided, we will use the default URL value, postgres://postgres@localhost:5432:

let addr = matches.value_of("database")
.unwrap_or("postgres://postgres@localhost:5432");
let conn = Connection::connect(addr, TlsMode::None)?;

We used a Connection::connect method with an address, and set the TlsMode parameter to TlsMode::None, because we don't use TLS in our demo. We created a Connection instance named conn to call our functions to interact with our database.

Finally, we can add branches for subcommands:

match matches.subcommand() {
(CMD_CREATE, _) => {
create_table(&conn)?;
}
(CMD_ADD, Some(matches)) => {
let name = matches.value_of("NAME").unwrap();
let email = matches.value_of("EMAIL").unwrap();
create_user(&conn, name, email)?;
}
(CMD_LIST, _) => {
let list = list_users(&conn)?;
for (name, email) in list {
println!("Name: {:20} Email: {:20}", name, email);
}
}
_ => {
matches.usage(); // but unreachable
}
}
Ok(())

The first branch matches the crate subcommand  and creates a table by calling the create_table function.

The second branch is for the add subcommand. It extracts pairs of required arguments for the name and email of a user, and calls the create_user function to create a user record with the provided values. We use unwrap to extract it, because both arguments are required.

The penultimate branch handles the list command and takes a list of users with the list_users function call. After the value has been taken, it is used in a for loop to print all the records of the users to the console.

The last branch is unreachable because we set AppSettings::SubcommandRequired to clap::App, but we leave it in for consistency. It is especially useful if you want to provide a default behavior when a subcommand value hasn't been set.