1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
use std::convert::TryFrom;
use std::iter::FromIterator;
use std::num::FpCategory;

use indexmap::IndexMap;
use serde_json as json;
use snafu::{ensure, Snafu};

use super::FileSource;
use crate::Value;

#[derive(Clone, Debug)]
pub struct Json {}

impl FileSource for Json {
    type Value = json::Value;
    type SerError = json::Error;
    type DeError = json::Error;

    fn extension(&self) -> String {
        "json".into()
    }

    fn deserialize(&self, string: &str) -> Result<Self::Value, Self::DeError> {
        json::from_str(&string)
    }

    fn serialize(&self, value: &Self::Value) -> Result<String, Self::SerError> {
        let mut string = json::to_string_pretty(value)?;
        string.push('\n');
        Ok(string)
    }
}

impl From<json::Value> for Value {
    fn from(value: json::Value) -> Value {
        match value {
            json::Value::Null => Value::Null,
            json::Value::Bool(bool) => Value::Bool(bool),
            json::Value::Number(number) => {
                if number.is_u64() {
                    Value::Uint(number.as_u64().unwrap())
                } else if number.is_i64() {
                    Value::Int(number.as_i64().unwrap())
                } else {
                    Value::Float(number.as_f64().unwrap())
                }
            }
            json::Value::String(string) => Value::String(string),
            json::Value::Array(array) => {
                Value::Array(Vec::from_iter(array.into_iter().map(Self::from)))
            }
            json::Value::Object(object) => Value::Object(IndexMap::from_iter(
                object
                    .into_iter()
                    .map(|(key, value)| (key, Self::from(value))),
            )),
        }
    }
}

#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)))]
pub enum IntoJsonError {
    #[snafu(display("Json does not support NaN (Not a Number) number values"))]
    Nan {},
    #[snafu(display("Json does not support Infinity number values"))]
    Infinite {},
}

impl TryFrom<Value> for json::Value {
    type Error = IntoJsonError;

    fn try_from(value: Value) -> Result<json::Value, Self::Error> {
        match value {
            Value::Null => Ok(json::Value::Null),
            Value::Bool(bool) => Ok(json::Value::Bool(bool)),
            Value::Int(int) => Ok(json::Value::Number(int.into())),
            Value::Uint(uint) => Ok(json::Value::Number(uint.into())),
            Value::Float(float) => {
                ensure!(float.classify() != FpCategory::Nan, Nan {});
                ensure!(float.classify() != FpCategory::Infinite, Infinite {});

                Ok(json::Value::Number(json::Number::from_f64(float).unwrap()))
            }
            Value::String(string) => Ok(json::Value::String(string)),
            Value::Array(array) => {
                let mut next_array = Vec::with_capacity(array.len());
                array
                    .into_iter()
                    .try_for_each(|item| -> Result<(), IntoJsonError> {
                        let next_item = Self::try_from(item)?;
                        next_array.push(next_item);
                        Ok(())
                    })?;
                Ok(json::Value::Array(next_array))
            }
            Value::Object(object) => {
                let mut next_map = json::map::Map::with_capacity(object.len());
                object
                    .into_iter()
                    .try_for_each(|(key, value)| -> Result<(), IntoJsonError> {
                        let next_value = Self::try_from(value)?;
                        next_map.insert(key, next_value);
                        Ok(())
                    })?;
                Ok(json::Value::Object(next_map))
            }
        }
    }
}