confitul 0.1.4

ConfitUL contains utilities for ConfitDB which is an experimental, distributed, real-time database, giving full control on conflict resolution.
Documentation
use crate::config_check::{ConfigCheck, ConfigCheckError, EmptyFieldError};
use gethostname::gethostname;
use serde::{Deserialize, Serialize};
use url::Url;

const DEFAULT_NAME: &str = "computer";

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LocalHostOptions {
    pub name: String,
    pub description: String,
    pub urls: Vec<String>,
}

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

    pub fn with_name(&self, name: &str) -> LocalHostOptions {
        match self.try_with_name(name) {
            Ok(updated) => updated,
            Err(_) => {
                let mut updated = self.clone();
                updated.name = Self::get_default_name();
                updated
            }
        }
    }

    pub fn try_with_name(&self, name: &str) -> Result<LocalHostOptions, ConfigCheckError> {
        let mut updated = self.clone();
        updated.name = String::from(name);
        updated.check()?;
        Ok(updated)
    }

    pub fn with_description(&self, description: &str) -> LocalHostOptions {
        self.try_with_description(description)
            .unwrap_or(self.clone())
    }

    pub fn try_with_description(
        &self,
        description: &str,
    ) -> Result<LocalHostOptions, ConfigCheckError> {
        let mut updated = self.clone();
        updated.description = String::from(description);
        updated.check()?;
        Ok(updated)
    }

    pub fn with_urls(&self, urls: &Vec<&str>) -> LocalHostOptions {
        self.try_with_urls(urls).unwrap_or(self.clone())
    }

    pub fn try_with_urls(&self, urls: &Vec<&str>) -> Result<LocalHostOptions, ConfigCheckError> {
        let mut updated = self.clone();
        let mut valid_urls: Vec<String> = Vec::new();
        for url in urls.iter() {
            match Url::parse(url) {
                Ok(_) => {
                    valid_urls.push(String::from(*url));
                }
                Err(e) => return Err(ConfigCheckError::Parse(e)),
            }
        }
        updated.urls = valid_urls;
        updated.check()?;
        Ok(updated)
    }

    fn get_default_name() -> String {
        match gethostname().to_str() {
            Some(hostname) => {
                if hostname.len() > 0 {
                    hostname
                } else {
                    DEFAULT_NAME
                }
            }
            None => DEFAULT_NAME,
        }
        .to_string()
    }
}

impl ConfigCheck for LocalHostOptions {
    fn check(&self) -> Result<LocalHostOptions, ConfigCheckError> {
        if self.name.len() == 0 {
            return Err(ConfigCheckError::Empty(EmptyFieldError::new("name")));
        }
        for url in self.urls.iter() {
            match Url::parse(url) {
                Ok(_) => {}
                Err(e) => return Err(ConfigCheckError::Parse(e)),
            }
        }
        Ok(self.clone())
    }
}

impl std::default::Default for LocalHostOptions {
    fn default() -> LocalHostOptions {
        LocalHostOptions {
            name: Self::get_default_name(),
            description: String::new(),
            urls: Vec::new(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::LocalHostOptions;
    use gethostname::gethostname;
    use serde_json;

    #[test]
    fn test_local_host_options_serde_json() {
        let options = LocalHostOptions::new();
        let serialized = serde_json::to_string(&options).unwrap();
        assert_eq!(
            format!(
                "{{\"name\":\"{}\",\"description\":\"\",\"urls\":[]}}",
                gethostname().to_str().unwrap()
            ),
            serialized
        );
    }
}