zero4rs 2.0.0

zero4rs is a powerful, pragmatic, and extremely fast web framework for Rust
Documentation
use lazy_static::lazy_static;

pub mod redis_client;
pub mod redis_client_cluster;
pub mod redis_client_sentinel;
pub mod redis_client_standalone;

pub mod with_rs;

pub use redis_client::*;
pub use with_rs::*;

lazy_static! {
    static ref RE: regex::Regex =
        regex::Regex::new(r#"^(redis(s)?)(-[a-z]+)?://((\w+)?(:(\w+))?@)?([^?]+)(\?([\w\W]*))?"#,)
            .unwrap();
}

#[derive(serde::Deserialize, Clone, Debug)]
pub struct RedisSettings {
    pub enable: Option<bool>,
    pub server: Option<String>,
}

#[derive(serde::Deserialize, Clone, Debug)]
pub struct Schema {
    pub schema: String,
    pub mode: String,
    pub username: String,
    pub passwd: String,
    pub host: Vec<String>,
    pub db: i64,
    pub opt: Vec<String>,
}

impl Default for RedisSettings {
    fn default() -> Self {
        Self {
            enable: Some(false),
            server: Some("rediss://127.0.0.1:6379/0".to_string()),
        }
    }
}

impl RedisSettings {
    pub fn is_enable(&self) -> bool {
        if let Some(enable) = &self.enable {
            *enable
        } else {
            false
        }
    }

    pub fn server(&self) -> String {
        self.server.clone().unwrap()
    }

    pub fn get(settings: &Option<RedisSettings>) -> Self {
        let _default = Self::default();

        if settings.is_some() {
            let mut s = settings.clone().unwrap();

            if s.enable.is_none() {
                s.enable = _default.enable;
            }

            if s.server.is_none() {
                s.server = _default.server;
            }

            s
        } else {
            _default
        }
    }
}

impl Schema {
    pub fn is_standalone(&self) -> bool {
        self.mode == "-standalone"
    }

    pub fn is_cluster(&self) -> bool {
        self.mode == "-cluster"
    }

    pub fn is_sentinel(&self) -> bool {
        self.mode == "-sentinel"
    }

    pub fn connection_string_standalone(&self) -> String {
        format!("{}://{}", self.schema, self.host[0])
    }

    pub fn connection_servers(&self) -> Vec<String> {
        self.host
            .iter()
            .map(|s| format!("{}://{}/", self.schema, s))
            .collect()
    }

    pub fn username(&self) -> Option<String> {
        if self.username.is_empty() {
            None
        } else {
            Some(self.username.to_owned())
        }
    }

    pub fn passwd(&self) -> Option<String> {
        if self.passwd.is_empty() {
            None
        } else {
            Some(self.passwd.to_owned())
        }
    }

    pub fn master_name(&self) -> String {
        let o: Vec<&String> = self
            .opt
            .iter()
            .filter(|&s| s.starts_with("master_name="))
            .collect::<Vec<_>>();

        if o.is_empty() {
            return "".to_string();
        }

        match o[0].find('=') {
            Some(i) => o[0][i + 1..].to_string(),
            None => "".to_string(),
        }
    }

    // parse_schema("redis://127.0.0.1:6379/0");
    // parse_schema("redis://keesh:cc@127.0.0.1:6379/0");
    // parse_schema("rediss://keesh:cc@127.0.0.1/0");
    // parse_schema("rediss-cluster://:cc@localhost:6381/1");
    // parse_schema("rediss-sentinel://keesh:cc@redis1.io:6380,redis2.io:6390/0?master_name=the_sentinal_master_name");
    pub fn parse_schema(connection_string: &str) -> Schema {
        let captures = RE.captures(connection_string).unwrap();

        let schema = captures.get(1).map_or("", |m| m.as_str());
        let mode = captures.get(3).map_or("-standalone", |m| m.as_str());
        let username = captures.get(5).map_or("", |m| m.as_str());
        let passwd = captures.get(7).map_or("", |m| m.as_str());
        let host = captures.get(8).map_or("127.0.0.1", |m| m.as_str());
        let opt = captures.get(10).map_or("", |m| m.as_str());

        let (host, db) = match host.find('/') {
            None => (host, "0"),
            Some(i) => (&host[..i], &host[i + 1..]),
        };

        let db = match db.parse::<i64>() {
            Ok(db) => db,
            Err(e) => {
                log::error!("db={}, connection_string={}", db, connection_string);
                panic!("parse_schema: error={:?}", e);
            }
        };

        let host: Vec<String> = host
            .split(',')
            .collect::<Vec<&str>>()
            .iter()
            .map(|&s| String::from(s.trim()))
            .collect();

        let opt: Vec<String> = opt
            .split('&')
            .collect::<Vec<&str>>()
            .iter()
            .filter(|s| !s.trim().is_empty())
            .map(|&s| String::from(s.trim()))
            .collect();

        Schema {
            schema: schema.to_owned(),
            mode: mode.to_owned(),
            username: username.to_owned(),
            passwd: passwd.to_owned(),
            host,
            db,
            opt,
        }
    }
}

// redis://[<username>][:<password>@]<hostname>[:port][/<db>]
impl std::fmt::Display for Schema {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}://", self.schema)?;

        if !self.username.is_empty() {
            f.write_str(&self.username)?;
        }

        if !self.passwd.is_empty() {
            write!(f, ":******@")?;
        }

        f.write_str(&self.host.join(","))?;

        write!(f, "/{}", self.db)?;

        if !self.opt.is_empty() {
            write!(f, "?{}", &self.opt.join("&"))?;
        }

        Ok(())
    }
}