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
112
113
114
115
116
//! JSON configuration parser.
//!
//! This is only usable if you enabled `json` Cargo feature.
//!
//! ### Example
//! ```rust
//! use plugx_config::parser::{ConfigurationParser, json::ConfigurationParserJson};
//! use plugx_input::Input;
//!
//! let bytes = br#"
//! {
//!     "hello": ["w", "o", "l", "d"],
//!     "foo": {
//!         "bar": {
//!             "baz": "Qux",
//!             "abc": 3.14
//!         },
//!         "xyz": false
//!     }
//! }
//! "#;
//!
//! let parser = ConfigurationParserJson::new();
//! // You can set nested key separator like this:
//! // parser.set_key_separator("__");
//! let parsed: Input = parser.try_parse(bytes.as_slice()).unwrap();
//! assert!(
//!     parsed.map_ref().unwrap().len() == 2 &&
//!     parsed.map_ref().unwrap().contains_key("foo") &&
//!     parsed.map_ref().unwrap().contains_key("hello")
//! );
//! let foo = parsed.map_ref().unwrap().get("foo").unwrap();
//! assert!(
//!     foo.map_ref().unwrap().len() == 2 &&
//!     foo.map_ref().unwrap().contains_key("bar") &&
//!     foo.map_ref().unwrap().contains_key("xyz")
//! );
//! let bar = foo.map_ref().unwrap().get("bar").unwrap();
//! assert_eq!(bar.map_ref().unwrap().get("baz").unwrap(), &"Qux".into());
//! assert_eq!(bar.map_ref().unwrap().get("abc").unwrap(), &3.14.into());
//! let xyz = foo.map_ref().unwrap().get("xyz").unwrap();
//! assert_eq!(xyz, &false.into());
//! let list = ["w", "o", "l", "d"].into();
//! assert_eq!(parsed.map_ref().unwrap().get("hello").unwrap(), &list);
//! ```
//!

use crate::{
    error::ConfigurationParserError,
    parser::{BoxedModifierFn, ConfigurationParser},
};
use plugx_input::Input;
use std::fmt::{Debug, Display, Formatter};

const NAME: &str = "JSON";
const SUPPORTED_FORMAT_LIST: &[&str] = &["json"];

#[derive(Default)]
pub struct ConfigurationParserJson {
    maybe_modifier: Option<BoxedModifierFn>,
}

impl Display for ConfigurationParserJson {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_str(NAME)
    }
}

impl Debug for ConfigurationParserJson {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("ConfigurationParserJson").finish()
    }
}

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

    pub fn set_modifier(&mut self, modifier: BoxedModifierFn) {
        self.maybe_modifier = Some(modifier);
    }

    pub fn with_modifier(mut self, modifier: BoxedModifierFn) -> Self {
        self.set_modifier(modifier);
        self
    }
}

impl ConfigurationParser for ConfigurationParserJson {
    fn supported_format_list(&self) -> Vec<String> {
        SUPPORTED_FORMAT_LIST
            .iter()
            .cloned()
            .map(Into::into)
            .collect()
    }

    fn try_parse(&self, bytes: &[u8]) -> Result<Input, ConfigurationParserError> {
        let mut result =
            serde_json::from_slice(bytes).map_err(|error| ConfigurationParserError::Parse {
                data: String::from_utf8_lossy(bytes).to_string(),
                parser: NAME.to_string(),
                supported_format_list: self.supported_format_list(),
                source: error.into(),
            })?;
        if let Some(ref modifier) = self.maybe_modifier {
            modifier(bytes, &mut result)?;
        }
        Ok(result)
    }

    fn is_format_supported(&self, bytes: &[u8]) -> Option<bool> {
        Some(serde_json::from_slice::<serde_json::Value>(bytes).is_ok())
    }
}