apolloconfig 0.1.0

apolloconfig
Documentation
use core::fmt;

use serde::{ser, Serialize};
use serde_json::{Map, Value};

use crate::types::NamespaceFormat;

//
pub const KEY_CONTENT: &str = "content";

//
#[derive(Debug, Clone)]
pub enum Configurations {
    Properties(Map<String, Value>),
    Json(Value),
    Txt(Box<str>),
    Other(Box<str>),
}

impl Configurations {
    pub fn from_map(
        map: &Map<String, Value>,
        namespace_format: NamespaceFormat,
    ) -> Result<Self, ConfigurationsFromMapError> {
        match namespace_format {
            NamespaceFormat::Properties => Ok(Self::Properties(map.to_owned())),
            NamespaceFormat::Xml
            | NamespaceFormat::Json
            | NamespaceFormat::Yml
            | NamespaceFormat::Yaml
            | NamespaceFormat::Txt => {
                let content = map
                    .get(KEY_CONTENT)
                    .ok_or(ConfigurationsFromMapError::ContentMissing)?;

                match namespace_format {
                    NamespaceFormat::Properties => unreachable!(),
                    NamespaceFormat::Xml => {
                        let content = content
                            .as_str()
                            .ok_or(ConfigurationsFromMapError::ContentTypeMismatch("String"))?;
                        Ok(Self::Other(content.into()))
                    }
                    NamespaceFormat::Json => {
                        let content = content
                            .as_str()
                            .ok_or(ConfigurationsFromMapError::ContentTypeMismatch("String"))?;
                        let value = serde_json::from_str(content)
                            .map_err(ConfigurationsFromMapError::JsonError)?;
                        Ok(Self::Json(value))
                    }
                    NamespaceFormat::Yml => {
                        let content = content
                            .as_str()
                            .ok_or(ConfigurationsFromMapError::ContentTypeMismatch("String"))?;
                        Ok(Self::Other(content.into()))
                    }
                    NamespaceFormat::Yaml => {
                        let content = content
                            .as_str()
                            .ok_or(ConfigurationsFromMapError::ContentTypeMismatch("String"))?;
                        Ok(Self::Other(content.into()))
                    }
                    NamespaceFormat::Txt => {
                        let content = content
                            .as_str()
                            .ok_or(ConfigurationsFromMapError::ContentTypeMismatch("String"))?;
                        Ok(Self::Txt(content.into()))
                    }
                }
            }
        }
    }

    pub fn to_map(&self) -> Result<Map<String, Value>, ConfigurationsToMapError> {
        match self {
            Configurations::Properties(map) => Ok(map.to_owned()),
            Configurations::Json(value) => {
                let s = serde_json::to_string(value)
                    .map_err(ser::Error::custom)
                    .map_err(ConfigurationsToMapError::JsonError)?;
                let mut map = Map::new();
                map.insert(KEY_CONTENT.to_string(), Value::String(s));
                Ok(map.to_owned())
            }
            Configurations::Txt(s) | Configurations::Other(s) => {
                let mut map = Map::new();
                map.insert(KEY_CONTENT.to_string(), Value::String(s.to_string()));
                Ok(map.to_owned())
            }
        }
    }
}

//
#[derive(Debug)]
pub enum ConfigurationsFromMapError {
    ContentMissing,
    ContentTypeMismatch(&'static str),
    JsonError(serde_json::Error),
}

impl fmt::Display for ConfigurationsFromMapError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

impl std::error::Error for ConfigurationsFromMapError {}

//
#[derive(Debug)]
pub enum ConfigurationsToMapError {
    JsonError(serde_json::Error),
}

impl fmt::Display for ConfigurationsToMapError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

impl std::error::Error for ConfigurationsToMapError {}

//
impl Serialize for Configurations {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer,
    {
        let map = self.to_map().map_err(ser::Error::custom)?;
        map.serialize(serializer)
    }
}

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

    #[derive(Serialize)]
    struct Foo {
        configurations: Configurations,
    }

    #[test]
    fn test_ser() {
        //
        let mut map = Map::new();
        map.insert("content".to_string(), Value::String("content".into()));
        assert_eq!(
            serde_json::to_string(&Foo {
                configurations: Configurations::Properties(map)
            })
            .unwrap(),
            r#"{"configurations":{"content":"content"}}"#
        );

        //
        let mut map = Map::new();
        map.insert("k".to_string(), Value::String("v".into()));
        assert_eq!(
            serde_json::to_string(&Foo {
                configurations: Configurations::Json(Value::Object(map))
            })
            .unwrap(),
            r#"{"configurations":{"content":"{\"k\":\"v\"}"}}"#
        );

        //
        assert_eq!(
            serde_json::to_string(&Foo {
                configurations: Configurations::Txt("test".into())
            })
            .unwrap(),
            r#"{"configurations":{"content":"test"}}"#
        );

        //
        assert_eq!(
            serde_json::to_string(&Foo {
                configurations: Configurations::Other("xxx".into())
            })
            .unwrap(),
            r#"{"configurations":{"content":"xxx"}}"#
        );
    }
}