Creating a pool

To create a connection pool, we will use the r2d2 crate that can hold multiple connections and provide one for us from the pool. This crate is generic, so you'll need a specific implementation for every database to connect to it. The r2d2 crate can connect to the following databases using adapter crates:

  • PostgreSQL
  • Redis
  • MySQL
  • SQLite
  • Neo4j
  • Diesel ORM
  • CouchDB
  • MongoDB
  • ODBC

For our example, we need the r2d2-postgres adapter crate to connect to the PostgreSQL database. Add it to our dependencies with the r2d2 crate:

[dependencies]
clap = "2.32"
csv = "1.0"
failure = "0.1"
postgres = "0.15"
r2d2 = "0.8"
r2d2_postgres = "0.14"
rayon = "1.0"
serde = "1.0"
serde_derive = "1.0"

We also keep the postgres dependency, and add failure for error-handling and rayon to execute SQL statements in parallel. We also added a set of serde crates to deserialize User records from the CSV file, along with the csv crate to read that file.

You will be much more comfortable using Rust structs that represent data records in a database. Let's add a User type that represents a user record in a database with the following struct:

#[derive(Deserialize, Debug)]
struct User {
name: String,
email: String,
}

Since we have our special User type, we can improve the create_user and list_users functions to use this new type:

fn create_user(conn: &Connection, user: &User) -> Result<(), Error> {
conn.execute("INSERT INTO users (name, email) VALUES ($1, $2)",
&[&user.name, &user.email])
.map(drop)
}

fn list_users(conn: &Connection) -> Result<Vec<User>, Error> {
let res = conn.query("SELECT name, email FROM users", &[])?.into_iter()
.map(|row| {
User {
name: row.get(0),
email: row.get(1),
}
})
.collect();
Ok(res)
}

It hasn't changed dramatically: we still use the same Connection type, but we use the fields from the User struct to fill our create statements and extract values from our get list query. The create_table function has not changed.

Add a constant for the import command:

const CMD_IMPORT: &str = "import";

Then, add it as a SubCommand to App:

.subcommand(SubCommand::with_name(CMD_IMPORT).about("import users from csv"))

Almost all branches have changed and we should explore those changes. The add command creates a User instance to call the create_user function:

(CMD_ADD, Some(matches)) => {
let name = matches.value_of("NAME").unwrap().to_owned();
let email = matches.value_of("EMAIL").unwrap().to_owned();
let user = User { name, email };
create_user(&conn, &user)?;
}

The list subcommand returns a list of User struct instances. We have to take this change into account:

(CMD_LIST, _) => {
let list = list_users(&conn)?;
for user in list {
println!("Name: {:20} Email: {:20}", user.name, user.email);
}
}

The import command is more complex, so let's discuss this in more detail in the following section.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.205.136