reool 0.13.0

An asynchrounous connection pool for Redis based on tokio and redis-rs
Documentation
use std::env;
use std::time::Duration;

use crate::activation_order::ActivationOrder;
use crate::config::PoolMode;
use crate::error::{InitializationError, InitializationResult};

fn make_prefix<T: Into<String>>(prefix: Option<T>) -> String {
    prefix
        .map(Into::into)
        .unwrap_or_else(|| "REOOL".to_string())
}

pub fn set_desired_pool_size<T, F>(prefix: Option<T>, mut f: F) -> InitializationResult<()>
where
    F: FnMut(usize) -> (),
    T: Into<String>,
{
    let prefix = make_prefix(prefix);

    let key = format!("{}_{}", prefix, "DESIRED_POOL_SIZE");
    match env::var(&key) {
        Ok(s) => {
            f(s.parse()
                .map_err(|err| InitializationError::new(key, Some(err)))?);
            Ok(())
        }
        Err(env::VarError::NotPresent) => Ok(()),
        Err(err) => Err(InitializationError::new(key, Some(err))),
    }
}

pub fn set_checkout_timeout<T, F>(prefix: Option<T>, mut f: F) -> InitializationResult<()>
where
    F: FnMut(Option<Duration>) -> (),
    T: Into<String>,
{
    let prefix = make_prefix(prefix);

    let key = format!("{}_{}", prefix, "CHECKOUT_TIMEOUT_MS");
    match env::var(&key).map(|s| s.to_uppercase()) {
        Ok(s) => {
            if s == "NONE" {
                f(None);
                Ok(())
            } else {
                f(Some(Duration::from_millis(s.parse().map_err(|err| {
                    InitializationError::new(key, Some(err))
                })?)));
                Ok(())
            }
        }
        Err(env::VarError::NotPresent) => Ok(()),
        Err(err) => Err(InitializationError::new(key, Some(err))),
    }
}

pub fn set_reservation_limit<T, F>(prefix: Option<T>, mut f: F) -> InitializationResult<()>
where
    F: FnMut(Option<usize>) -> (),
    T: Into<String>,
{
    let prefix = make_prefix(prefix);

    let key = format!("{}_{}", prefix, "RESERVATION_LIMIT");
    match env::var(&key).map(|s| s.to_uppercase()) {
        Ok(s) => {
            if s == "NONE" {
                f(None);
                Ok(())
            } else {
                f(Some(s.parse().map_err(|err| {
                    InitializationError::new(key, Some(err))
                })?));
                Ok(())
            }
        }
        Err(env::VarError::NotPresent) => Ok(()),
        Err(err) => Err(InitializationError::new(key, Some(err))),
    }
}

pub fn set_stats_interval<T, F>(prefix: Option<T>, mut f: F) -> InitializationResult<()>
where
    F: FnMut(Duration) -> (),
    T: Into<String>,
{
    let prefix = make_prefix(prefix);

    let key = format!("{}_{}", prefix, "STATS_INTERVAL_MS");
    match env::var(&key) {
        Ok(s) => {
            let ms: u64 = s
                .parse()
                .map_err(|err| InitializationError::new(key, Some(err)))?;
            f(Duration::from_millis(ms));
            Ok(())
        }
        Err(env::VarError::NotPresent) => Ok(()),
        Err(err) => Err(InitializationError::new(key, Some(err))),
    }
}

pub fn set_min_required_nodes<T, F>(prefix: Option<T>, mut f: F) -> InitializationResult<()>
where
    F: FnMut(usize) -> (),
    T: Into<String>,
{
    let prefix = make_prefix(prefix);

    let key = format!("{}_{}", prefix, "MIN_REQUIRED_NODES");
    match env::var(&key) {
        Ok(s) => {
            f(s.parse()
                .map_err(|err| InitializationError::new(key, Some(err)))?);
            Ok(())
        }
        Err(env::VarError::NotPresent) => Ok(()),
        Err(err) => Err(InitializationError::new(key, Some(err))),
    }
}

pub fn set_activation_order<T, F>(prefix: Option<T>, mut f: F) -> InitializationResult<()>
where
    F: FnMut(ActivationOrder) -> (),
    T: Into<String>,
{
    let prefix = make_prefix(prefix);

    let key = format!("{}_{}", prefix, "ACTIVATION_ORDER");
    match env::var(&key) {
        Ok(s) => {
            f(s.parse()
                .map_err(|err| InitializationError::new(key, Some(err)))?);
            Ok(())
        }
        Err(env::VarError::NotPresent) => Ok(()),
        Err(err) => Err(InitializationError::new(key, Some(err))),
    }
}

pub fn set_pool_mode<T, F>(prefix: Option<T>, mut f: F) -> InitializationResult<()>
where
    F: FnMut(PoolMode) -> (),
    T: Into<String>,
{
    let prefix = make_prefix(prefix);

    let key = format!("{}_{}", prefix, "POOL_MODE");
    match env::var(&key) {
        Ok(s) => {
            f(s.parse()
                .map_err(|err| InitializationError::new(key, Some(err)))?);
            Ok(())
        }
        Err(env::VarError::NotPresent) => Ok(()),
        Err(err) => Err(InitializationError::new(key, Some(err))),
    }
}

pub fn get_connect_to<T>(prefix: Option<T>) -> InitializationResult<Option<Vec<String>>>
where
    T: Into<String>,
{
    let prefix = make_prefix(prefix);

    let key = format!("{}_{}", prefix, "CONNECT_TO");
    let s = match env::var(&key) {
        Ok(s) => s,
        Err(env::VarError::NotPresent) => return Ok(None),
        Err(err) => return Err(InitializationError::new(key, Some(err))),
    };

    let parts = parse_connect_to(&s);

    if !parts.is_empty() {
        Ok(Some(parts))
    } else {
        Err(InitializationError::message_only(format!(
            "Found '{}' but it is empty",
            key
        )))
    }
}

fn parse_connect_to(what: &str) -> Vec<String> {
    what.split(';')
        .filter(|s| !s.is_empty())
        .map(str::trim)
        .map(ToOwned::to_owned)
        .collect()
}

#[test]
fn prefix_reool_is_default() {
    let prefix = make_prefix::<String>(None);

    assert_eq!(prefix, "REOOL");
}
#[test]
fn prefix_can_be_customized() {
    let prefix = make_prefix(Some("TEST"));

    assert_eq!(prefix, "TEST");
}

#[test]
fn parse_connect_to_empty() {
    let res = parse_connect_to("");
    assert_eq!(res, Vec::<String>::new());
}

#[test]
fn parse_connect_to_one() {
    let res = parse_connect_to("redis://127.0.0.1:6379");
    assert_eq!(res, vec!["redis://127.0.0.1:6379".to_string()]);
}

#[test]
fn parse_connect_to_two() {
    let res = parse_connect_to("redis://127.0.0.1:6379;redis://127.0.0.1:6380");
    assert_eq!(
        res,
        vec![
            "redis://127.0.0.1:6379".to_string(),
            "redis://127.0.0.1:6380".to_string()
        ]
    );
}