File formats and databases

The standard library offers the std::fs module for filesystem manipulation, which we explored in Chapter 11, Exploring the Standard Library.

For databases, there are many crates available for working with:

Here is some code (mysql.rs) that connects to a local Mysql database, creates a payment table, and does some insert operations, and then finally reads back from that table:

#[macro_use] 
extern crate mysql; 
 
#[derive(Debug, PartialEq, Eq)] 
struct Payment { 
    customer_id: i32, 
    amount: i32, 
    account_name: Option<String>, 
} 
 
fn main() { 
    let pool = mysql::Pool::new("mysql://root:password@localhost:3307").unwrap(); 
 
    // Let's create a payment table. 
    // It is temporary so we do not need `tmp` database to exist. 
    // Unwap just to make sure no error happened. 
    pool.prep_exec(r"CREATE TEMPORARY TABLE tmp.payment ( 
                         customer_id int not null, 
                         amount int not null, 
                         account_name text 
                     )", ()).unwrap(); 
 
    let payments = vec![ 
 Payment { customer_id: 1, amount: 2, account_name: None }, 
 Payment { customer_id: 3, amount: 4, account_name: Some("foo".into()) }, 
 Payment { customer_id: 5, amount: 6, account_name: None }, 
 Payment { customer_id: 7, amount: 8, account_name: None }, 
 Payment { customer_id: 9, amount: 10, account_name: Some("bar".into()) }, 
    ]; 
 
    // Let's insert payments to the database 
    // We will use into_iter() because we do not need to map Stmt to anything else. 
    // Also we assume that no error happened in `prepare`. 
    for mut stmt in pool.prepare(r"INSERT INTO tmp.payment 
     (customer_id, amount, account_name) 
                            VALUES 
     (:customer_id, :amount, :account_name)").into_iter() { 
        for p in payments.iter() { 
            // `execute` takes ownership of `params` so we pass account name by reference. 
            // Unwrap each result just to make sure no errors happened. 
            stmt.execute(params!{ 
                "customer_id" => p.customer_id, 
                "amount" => p.amount, 
                "account_name" => &p.account_name, 
            }).unwrap(); 
        } 
    } 
 
    // Let's select payments from database 
    let selected_payments: Vec<Payment> = 
    pool.prep_exec("SELECT customer_id, amount, account_name from tmp.payment", ()) 
    .map(|result| { // In this closure we will map `QueryResult` to `Vec<Payment>` 
        // `QueryResult` is iterator over `MyResult<row, err>` so first call to `map` 
        // will map each `MyResult` to contained `row` (no proper error handling) 
        // and second call to `map` will map each `row` to `Payment` 
        result.map(|x| x.unwrap()).map(|row| { 
            let (customer_id, amount, account_name) = mysql::from_row(row); 
            Payment { 
                customer_id: customer_id, 
                amount: amount, 
                account_name: account_name, 
            } 
        }).collect() // Collect payments so now `QueryResult` is mapped to `Vec<Payment>` 
    }).unwrap(); // Unwrap `Vec<Payment>` 
} 

To get an idea how this works, here is some code (mongodb.rs) that connects to a local MongoDB and does some insert, update, delete, and find operations on a collection of movies:

let client = Client::connect("localhost", 27017) 
    .expect("Failed to initialize client."); 
 
let coll = client.db("media").collection("movies"); 
coll.insert_one(doc!{ "title" => "Back to the Future" }, None).unwrap(); 
coll.update_one(doc!{}, doc!{ "director" => "Robert Zemeckis" }, None).unwrap(); 
coll.delete_many(doc!{}, None).unwrap(); 
 
let mut cursor = coll.find(None, None).unwrap(); 
for result in cursor { 
    if let Ok(item) = result { 
        if let Some(&Bson::String(ref title)) = item.get("title") { 
            println!("title: {}", title); 
        } 
    } 
}