datasources 0.1.5

Async datasource interfaces, middleware, registry, and test doubles
Documentation
use crate::{
    Command, ConvertSchema, Mapper, MockCommand, Native, Parameter, Row, RowTypeConverter, TransformMapper,
    Value,
};

fn row(pairs: &[(&str, Value)]) -> Row {
    pairs
        .iter()
        .map(|(key, value)| ((*key).to_string(), value.clone()))
        .collect()
}

struct NameValue;

impl ConvertSchema for NameValue {
    fn schema() -> Vec<(&'static str, &'static str)> {
        vec![("name", "TEXT"), ("value", "TEXT")]
    }
}

struct UserRow;

impl ConvertSchema for UserRow {
    fn schema() -> Vec<(&'static str, &'static str)> {
        vec![
            ("id", "INTEGER"),
            ("username", "TEXT"),
            ("email", "TEXT"),
            ("contact_name1", "TEXT"),
            ("contact_name2", "TEXT"),
        ]
    }
}

fn user_contacts_mapper() -> TransformMapper<Row, Row> {
    TransformMapper::new(
        |mut row: Row| {
            let first = row.get("contact_name1").cloned().unwrap_or(Value::Null);
            let second = row.get("contact_name2").cloned().unwrap_or(Value::Null);
            row.insert(
                "contacts".into(),
                Value::from(format!(
                    "{},{}",
                    match first {
                        Value::Text(text) => text,
                        _ => String::new(),
                    },
                    match second {
                        Value::Text(text) => text,
                        _ => String::new(),
                    }
                )),
            );
            row
        },
        |row| row,
    )
}

#[tokio::test]
async fn execute_reader_returns_name_value_objects() {
    let command = MockCommand::new();
    command.reader_rows.lock().unwrap().push(row(&[
        ("name", Value::from("theme")),
        ("value", Value::from("dark")),
    ]));
    let reader = command
        .execute_reader("SELECT name, value FROM settings", None)
        .await
        .unwrap();
    let converter = RowTypeConverter::of::<NameValue>();
    let mut items = Vec::new();
    while reader.next().await.unwrap() {
        items.push(converter.convert(&reader.current().unwrap()).unwrap());
    }
    assert_eq!(items[0]["name"], Value::from("theme"));
    assert_eq!(items[0]["value"], Value::from("dark"));
}

#[tokio::test]
async fn execute_insert_update_delete() {
    let command = MockCommand::new();
    command
        .execute_native(
            &Native::new("insert"),
            Some(&[Parameter::new("id", 1_i64), Parameter::new("name", "a")]),
        )
        .await
        .unwrap();
    command
        .execute_native(
            &Native::new("update"),
            Some(&[Parameter::new("id", 1_i64), Parameter::new("name", "A")]),
        )
        .await
        .unwrap();
    command
        .execute_native(&Native::new("delete"), Some(&[Parameter::new("id", 1_i64)]))
        .await
        .unwrap();
    assert_eq!(command.execute_logs.lock().unwrap()[0].0, "insert");
    assert_eq!(command.execute_logs.lock().unwrap()[1].0, "update");
    assert_eq!(command.execute_logs.lock().unwrap()[2].0, "delete");
    assert_eq!(
        command.execute_logs.lock().unwrap()[0]
            .1
            .iter()
            .map(|parameter| parameter.name.as_str())
            .collect::<Vec<_>>(),
        ["id", "name"]
    );
}

#[tokio::test]
async fn execute_reader_maps_flat_contacts() {
    let command = MockCommand::new();
    command.reader_rows.lock().unwrap().push(row(&[
        ("id", Value::Int(1)),
        ("username", Value::from("bobby")),
        ("email", Value::from("bob@asdf.com")),
        ("contact_name1", Value::from("bob")),
        ("contact_name2", Value::from("roberta")),
    ]));
    let reader = command
        .execute_reader(
            "SELECT contact_name1, contact_name2, email, id, username FROM users",
            None,
        )
        .await
        .unwrap();
    let converter = RowTypeConverter::of::<UserRow>();
    let mapper = user_contacts_mapper();
    let mut user = None;
    while reader.next().await.unwrap() {
        user = Some(Mapper::map(
            &mapper,
            converter.convert(&reader.current().unwrap()).unwrap(),
        ));
    }
    assert_eq!(user.as_ref().unwrap()["username"], Value::from("bobby"));
    assert_eq!(user.as_ref().unwrap()["email"], Value::from("bob@asdf.com"));
    assert_eq!(
        user.as_ref().unwrap()["contacts"],
        Value::from("bob,roberta")
    );
}