embedded_redis_client 0.1.0

Automatically runs a local redis-server instance
Documentation
use std::time::{Duration, Instant};

// use std::string::ToString;
// use strum_macros;

use super::settings::{RedisServerConnectionSettings, RedisServerSettings};
use super::states::{
    CheckingRedisServerEnvironment, FaultyRedisServer, ReadyRedisServerProcess, ReadyToStart,
    RedisServerRunningInAnotherProcess, StartedRedisServerProcess,
};
use crate::error::{EmbeddedRedisClientError, ErrorType};

// #[derive(strum_macros::ToString)]
pub enum EmbeddedRedisServer {
    CheckingEnvironment(CheckingRedisServerEnvironment),
    RunningInAnotherProcess(RedisServerRunningInAnotherProcess),
    ReadyToStart(ReadyToStart),
    Started(StartedRedisServerProcess),
    Ready(ReadyRedisServerProcess),
    Faulty(FaultyRedisServer),
}

// trait RedisServerTrait {
//     pub fn start() {

//     }
// }

impl EmbeddedRedisServer {
    pub fn start(
        settings: RedisServerSettings,
    ) -> Result<EmbeddedRedisServer, EmbeddedRedisClientError> {
        let ready_to_start: ReadyToStart;
        let redis_server = CheckingRedisServerEnvironment::start(
            settings.connection.clone(),
            settings.startup.startup_timeout,
        )?;
        match redis_server {
            EmbeddedRedisServer::RunningInAnotherProcess(_) => return Ok(redis_server),
            EmbeddedRedisServer::ReadyToStart(ready) => {
                ready_to_start = ready;
            }
            _ => {
                return Err(EmbeddedRedisClientError::new(
                    ErrorType::Other,
                    String::from("Incorrect start-up sequence"),
                ))
            }
        }

        let started_server =
            StartedRedisServerProcess::start(ready_to_start, settings.startup.clone())?;
        let ready_server = ReadyRedisServerProcess::new(started_server, settings)?;
        Ok(EmbeddedRedisServer::Ready(ready_server))
    }

    pub fn start_with_retry(
        settings: RedisServerSettings,
    ) -> Result<EmbeddedRedisServer, EmbeddedRedisClientError> {
        let mut number_of_startup_attempts = 0;
        let mut startup_error = EmbeddedRedisClientError::new(ErrorType::Other, String::from(""));
        while number_of_startup_attempts < settings.startup.maximum_startup_retries {
            match Self::start(settings.clone()) {
                Ok(redis_server) => return Ok(redis_server),
                Err(error) => {
                    number_of_startup_attempts += 1;
                    startup_error = error;
                }
            }
        }
        return Err(EmbeddedRedisClientError::new(
            ErrorType::ServerStartup(Box::new(startup_error)),
            format! {"Redis server startup failed after {} attempts",number_of_startup_attempts},
        ));
    }

    pub fn state(&self) -> String {
        match self {
            EmbeddedRedisServer::CheckingEnvironment(_) => String::from("CheckingEnvironment"),
            EmbeddedRedisServer::RunningInAnotherProcess(_) => {
                String::from("RunningInAnotherProcess")
            }
            EmbeddedRedisServer::ReadyToStart(_) => String::from("ReadyToStart"),
            EmbeddedRedisServer::Started(_) => String::from("Started"),
            EmbeddedRedisServer::Ready(_) => String::from("Ready"),
            EmbeddedRedisServer::Faulty(_) => String::from("Faulty"),
        }
    }
}

pub mod connection {
    // use futures::prelude::*;

    use redis;

    use super::*;

    // use crate::redis_server::RedisServerConnectionSettings;
    // use crate::error::RedisGraphClientError;

    pub fn get_redis_server_connection(
        connection_settings: RedisServerConnectionSettings,
    ) -> Result<redis::Connection, EmbeddedRedisClientError> {
        let client = redis::Client::open(connection_settings)?;
        let connection = client.get_connection()?;
        Ok(connection)
    }

    pub async fn get_multiplexed_redis_server_connection(
        connection_settings: RedisServerConnectionSettings,
    ) -> Result<redis::aio::MultiplexedConnection, EmbeddedRedisClientError> {
        let client = redis::Client::open(connection_settings).unwrap();
        let connection: redis::aio::MultiplexedConnection =
            client.get_multiplexed_async_std_connection().await?;
        Ok(connection)
    }

    pub fn try_to_get_redis_server_connection_until_timeout(
        connection_settings: RedisServerConnectionSettings,
        timeout: Duration,
    ) -> Result<redis::Connection, EmbeddedRedisClientError> {
        let time_start_polling = Instant::now();
        let client = redis::Client::open(connection_settings)?;
        while Instant::now() - time_start_polling < timeout {
            match client.get_connection() {
                Ok(server_connection) => return Ok(server_connection),
                Err(_) => continue,
            }
        }
        Err(EmbeddedRedisClientError::new(
            ErrorType::ServerTimeout,
            format!("Unable to connect to redis server within {:#?}", timeout),
        ))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::redis_server_settings_for_testing;

    #[test]
    fn start_redis_server() {
        let _redis_server = EmbeddedRedisServer::start(redis_server_settings_for_testing()).unwrap();
    }

    // #[test]
    // fn test_redis_connection() {
    //     let redis_server = RedisServer::start(redis_server_settings_for_testing()).unwrap();

    //     let test_key = "test_connection";
    //     let value_to_write: u8 = 1;
    //     let _: () = redis_server
    //         .client_connection
    //         .set(test_key, value_to_write)
    //         .await
    //         .unwrap();

    //     let retrieved_value = redis_server
    //         .client_connection
    //         .get(test_key)
    //         .await
    //         .unwrap();

    //     let _: () = redis_server
    //         .client_connection
    //         .del(test_key)
    //         .await
    //         .unwrap();

    //     assert_eq!(value_to_write, retrieved_value);
    // }
}