datasources 0.1.5

Async datasource interfaces, middleware, registry, and test doubles
Documentation
use crate::command_middleware::CommandMiddleware;
use crate::parameter::Parameter;
use crate::response::Response;
use crate::result::Result;
use crate::row::Row;
use crate::value::Value;
use std::any::Any;
use std::future::Future;
use std::sync::{Arc, Mutex};
use std::time::Instant;

#[derive(Clone, Default)]
pub struct CommandMiddlewareHost {
    middlewares: Arc<Mutex<Vec<Arc<dyn CommandMiddleware>>>>,
}

impl CommandMiddlewareHost {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn add_middleware(&self, middleware: Arc<dyn CommandMiddleware>) {
        self.middlewares
            .lock()
            .expect("middleware")
            .push(middleware);
    }

    pub async fn run<T: 'static, F, Fut>(
        &self,
        text: &str,
        params: Option<&[Parameter]>,
        fn_op: F,
    ) -> Result<T>
    where
        F: FnOnce(String, Option<Vec<Parameter>>) -> Fut,
        Fut: Future<Output = Result<(T, Vec<Row>)>>,
    {
        let mut text = text.trim().to_string();
        let mut params = params.map(ToOwned::to_owned);
        let middlewares = self.middlewares.lock().expect("middleware").clone();
        for middleware in &middlewares {
            if let Some(next) = middleware.before_command(&text, params.as_deref()).await? {
                (text, params) = next;
            }
        }

        let start = Instant::now();
        let outcome = fn_op(text.clone(), params.clone()).await;
        let elapsed_ms = start.elapsed().as_secs_f64() * 1000.0;
        let execute_rows = match &outcome {
            Ok((value, rows)) if rows.is_empty() => response_row(value),
            _ => Vec::new(),
        };
        let (rows, error) = match &outcome {
            Ok((_, rows)) if !rows.is_empty() => (rows.as_slice(), None),
            Ok((_, rows)) => (
                if execute_rows.is_empty() {
                    rows.as_slice()
                } else {
                    execute_rows.as_slice()
                },
                None,
            ),
            Err(error) => (&[] as &[Row], Some(error)),
        };
        for middleware in &middlewares {
            middleware
                .after_command(&text, params.as_deref(), rows, elapsed_ms, error)
                .await?;
        }
        outcome.map(|(value, _)| value)
    }
}

fn response_row<T: 'static>(value: &T) -> Vec<Row> {
    (value as &dyn Any)
        .downcast_ref::<Response>()
        .map(|response| {
            vec![[
                (
                    "result".into(),
                    match &response.result {
                        Value::Null => Value::Text("null".into()),
                        other => other.clone(),
                    },
                ),
                ("rowsAffected".into(), Value::Int(response.rows_affected)),
            ]
            .into_iter()
            .collect()]
        })
        .unwrap_or_default()
}