#[macro_use]
extern crate serde_json;

use serde::{Deserialize, Serialize};
use serde_json::{Result, Value};
use chrono::{DateTime, Utc, TimeZone};

// alternative JSON for human: https://crates.io/crates/json5
mod my_date_format {
    use chrono::{DateTime, Utc, TimeZone};
    use serde::{self, Deserialize, Serializer, Deserializer};

    const FORMAT: &'static str = "%Y-%m-%d %H:%M:%S";

    pub fn serialize<S>(date: &DateTime<Utc>, serializer: S ) -> Result<S::Ok, S::Error> where S: Serializer {
        let s = format!("{}", date.format(FORMAT));
        serializer.serialize_str(&s)
    }

    pub fn deserialize<'de, D>(deserializer: D ) -> Result<DateTime<Utc>, D::Error> where D: Deserializer<'de> {
        let s = String::deserialize(deserializer)?;
        Utc.datetime_from_str(&s, FORMAT).map_err(serde::de::Error::custom)
    }
}

// https://serde.rs/container-attrs.html
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
enum TestEnum {

    #[serde(rename = "A")]
    TypeA,

    // #[serde(rename(serialize = "ser_name"))]
    // #[serde(rename(deserialize = "de_name"))]
    // #[serde(rename(serialize = "ser_name", deserialize = "de_name"))]
    #[serde(rename = "B")]
    TypeB,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
struct TestStruct {
    ty: TestEnum,
    #[serde(with = "my_date_format")]
    timestamp: DateTime<Utc>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
// The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase"
// , "snake_case", "SCREAMING_SNAKE_CASE", "kebab-case", "SCREAMING-KEBAB-CASE".
struct TestStruct2 {
    test_id: i32,
    test_name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    test_description: Option<String>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum StringOrStringArray {
    String(String),
    Vec(Vec<String>),
}

#[derive(Clone, Debug, Serialize, Deserialize)]
struct TestStruct3 {
    sub: StringOrStringArray,
}

fn main() -> Result<()> {
    let serialized = serde_json::to_string(&TestStruct{
        ty: TestEnum::TypeA,
        timestamp: Utc.datetime_from_str("2020-01-28 17:04:00", "%Y-%m-%d %H:%M:%S").map_err(serde::de::Error::custom)?,
    })?;

    println!("{}", serialized);

    let data = r#"
        {
            "name": "John Doe",
            "age": 43,
            "phones": [
                "+44 1234567",
                "+44 2345678"
            ]
        }"#;
    let v: Value = serde_json::from_str(data)?;
    println!("{}", v);

    let mut john = json!({
        "name": "John Doe",
        "age": 43,
        "phones": [
            "+44 1234567",
            "+44 2345678"
        ]
    });
    println!("{}", &john);

    if let Some(ref mut m) = john.as_object_mut() {
        m.insert("test".into(), Value::Bool(true));
        m.insert("test2".into(), Value::String("hello world".into()));
    }
    println!("{}", john);

    let test_struct2 = TestStruct2 {
        test_id: 1,
        test_name: "Hatter".into(),
        test_description: None,
    };
    println!("{}", serde_json::to_string(&test_struct2)?);

    println!();
    let test_struct3_1: TestStruct3 = serde_json::from_str("{\"sub\": \"name\"}")?;
    println!("{}", serde_json::to_string_pretty(&test_struct3_1)?);
    let test_struct3_2: TestStruct3 = serde_json::from_str("{\"sub\": [\"name1\", \"name2\"]}")?;
    println!("{}", serde_json::to_string_pretty(&test_struct3_2)?);

    Ok(())
}
