datasources 0.1.5

Async datasource interfaces, middleware, registry, and test doubles
Documentation
use crate::datasource_error::DatasourceError;
use crate::i_where::IWhere;
use crate::id_key::IdKey;
use crate::read_input::{where_of, ReadInput};
use crate::response::Response;
use crate::result::Result;
use crate::row::Row;
use crate::statements::{Operator, ExpressionEvaluator, Where};
use crate::value::Value;
use std::sync::{Arc, Mutex};

pub struct SharedItems {
    pub items: Mutex<Vec<Row>>,
    next_id: Mutex<i64>,
}

impl SharedItems {
    #[allow(dead_code)]
    pub fn new(items: Vec<Row>) -> Arc<Self> {
        let next_id = items
            .iter()
            .filter_map(|row| match row.get("id") {
                Some(Value::Int(id)) => Some(*id),
                _ => None,
            })
            .max()
            .unwrap_or(0)
            + 1;
        Arc::new(Self {
            items: Mutex::new(items),
            next_id: Mutex::new(next_id),
        })
    }

    pub fn filter(&self, where_clause: Option<&dyn IWhere>) -> Vec<Row> {
        let items = self.items.lock().expect("items");
        match where_clause {
            None => items.clone(),
            Some(predicate) => {
                let evaluator = ExpressionEvaluator;
                items
                    .iter()
                    .filter(|row| {
                        predicate
                            .as_any()
                            .downcast_ref::<Where>()
                            .map(|statement| {
                                evaluator.evaluate(&statement.condition, row) == Value::Bool(true)
                            })
                            .unwrap_or(true)
                    })
                    .cloned()
                    .collect()
            }
        }
    }

    pub fn filter_input(&self, query: Option<&dyn ReadInput>) -> Vec<Row> {
        self.filter(where_of(query))
    }

    pub fn insert(&self, item: &mut Row) -> Row {
        if !matches!(item.get("id"), Some(Value::Int(id)) if *id != 0) {
            let mut next = self.next_id.lock().expect("items");
            let id = *next;
            *next += 1;
            item.insert("id".into(), Value::Int(id));
        }
        self.items.lock().expect("items").push(item.clone());
        item.clone()
    }

    pub fn save(&self, item: &Row) -> Response {
        let id = item.get("id").cloned();
        let mut items = self.items.lock().expect("items");
        if let Some(index) = items.iter().position(|row| row.get("id") == id.as_ref()) {
            items[index] = item.clone();
        }
        Response::new(Value::Null, 1, None)
    }

    pub fn remove(&self, item: &Row, by_where: Option<&dyn IWhere>) -> Response {
        let mut items = self.items.lock().expect("items");
        if let Some(predicate) = by_where {
            let evaluator = ExpressionEvaluator;
            items.retain(|row| {
                !predicate
                    .as_any()
                    .downcast_ref::<Where>()
                    .map(|statement| {
                        evaluator.evaluate(&statement.condition, row) == Value::Bool(true)
                    })
                    .unwrap_or(false)
            });
        } else {
            let id = item.get("id").cloned();
            items.retain(|row| row.get("id") != id.as_ref());
        }
        Response::new(Value::Null, 1, None)
    }

    pub fn by_id(&self, id: &dyn IdKey) -> Result<Option<Row>> {
        let clause = id_clause(id)?;
        Ok(self.filter(Some(&clause)).into_iter().next())
    }
}

fn id_clause(id: &dyn IdKey) -> Result<Where> {
    match (id.as_value(), id.as_row()) {
        (Some(value), _) => Ok(Where::new(Operator::equals("id", value.clone()))),
        (_, Some(row)) => row
            .iter()
            .map(|(key, value)| Operator::equals(key.clone(), value.clone()))
            .reduce(|left, right| Operator::and(left, right))
            .map(Where::new)
            .ok_or_else(|| DatasourceError::message("ReadSingleById requires an Id column")),
        _ => Err(DatasourceError::message(
            "ReadSingleById requires an Id column",
        )),
    }
}