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
use serde_json::{Map, Value};

pub trait Raw {
    fn raw(&self) -> &Value;
}

pub trait Property {
    fn get_property(&self, full_path: &str) -> Option<&Value>;
    fn get_str_property(&self, full_path: &str) -> Option<&str>;
    fn get_string_property(&self, full_path: &str) -> Option<String>;
    fn get_int_property(&self, full_path: &str) -> Option<i64>;
    fn get_float_property(&self, full_path: &str) -> Option<f64>;
    fn get_array_property(&self, full_path: &str) -> Option<&Vec<Value>>;
    fn get_object_property(&self, full_path: &str) -> Option<&Map<String, Value>>;
}

impl<T> Property for T
where
    T: Raw,
{
    fn get_property(&self, full_path: &str) -> Option<&Value> {
        if full_path.is_empty() {
            return Some(self.raw());
        }

        let paths = full_path.split('.');
        let mut cur_raw = self.raw();

        for path in paths {
            if cur_raw.is_array() {
                if let Ok(idx) = path.parse::<usize>() {
                    match cur_raw.get(idx) {
                        Some(new_raw) => cur_raw = new_raw,
                        None => return None,
                    };
                    continue;
                }
            }

            match cur_raw.get(path) {
                Some(new_raw) => cur_raw = new_raw,
                None => return None,
            }
        }

        Some(cur_raw)
    }

    fn get_str_property(&self, full_path: &str) -> Option<&str> {
        self.get_property(full_path)
            .and_then(serde_json::Value::as_str)
    }

    fn get_string_property(&self, full_path: &str) -> Option<String> {
        self.get_property(full_path)
            .and_then(serde_json::Value::as_str)
            .map(std::string::ToString::to_string)
    }

    fn get_int_property(&self, full_path: &str) -> Option<i64> {
        self.get_property(full_path)
            .and_then(serde_json::Value::as_i64)
            .or_else(|| {
                self.get_property(full_path)
                    .and_then(serde_json::Value::as_f64)
                    .map(|v| v as i64)
            })
    }

    fn get_float_property(&self, full_path: &str) -> Option<f64> {
        self.get_property(full_path)
            .and_then(serde_json::Value::as_f64)
            .or_else(|| {
                self.get_property(full_path)
                    .and_then(serde_json::Value::as_i64)
                    .map(|v| v as f64)
            })
    }

    fn get_array_property(&self, full_path: &str) -> Option<&Vec<Value>> {
        self.get_property(full_path)
            .and_then(serde_json::Value::as_array)
    }

    fn get_object_property(&self, full_path: &str) -> Option<&Map<String, Value>> {
        self.get_property(full_path)
            .and_then(serde_json::Value::as_object)
    }
}

impl Raw for Value {
    fn raw(&self) -> &Value {
        self
    }
}