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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
use std::fmt::{Display, Formatter, Error, Debug};
use serde::{Deserialize, Deserializer};

/// A value that has to be included in the JSON
/// string but can contain the value null.
///
/// Some values in discord can be omitted from
/// the JSON string, these are represented as
/// Option<T>. Some other values must be in the
/// JSON string but may contain the null value
/// and should be represented using Option<Option<T>>.
pub fn double_option<'de, T, D>(de: D) -> Result<Option<Option<T>>, D::Error>
    where T: Deserialize<'de>,
          D: Deserializer<'de>
{
    Deserialize::deserialize(de).map(Some)
}

/// Represents an error relative to a JSON string.
/// The error usually means that the provided JSON is
/// not a correctly formatted JSON string.
pub struct JsonError {
    pub msg: String,

    #[cfg(feature = "backtrace")]
    pub backtrace: String,
}

impl JsonError {
    pub fn new<S>(msg: S) -> JsonError where S: Into<String> {
        JsonError {
            msg: msg.into(),

            #[cfg(feature = "backtrace")]
            backtrace: format!("{:#?}", backtrace::Backtrace::new()),
        }
    }

    pub fn err<S, T>(msg: S) -> Result<T, JsonError> where S: Into<String> {
        Err(JsonError {
            msg: msg.into(),

            #[cfg(feature = "backtrace")]
            backtrace: format!("{:#?}", backtrace::Backtrace::new()),
        })
    }
}

impl Display for JsonError {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        #[cfg(not(feature = "backtrace"))] return write!(f, "{}", self.msg);
        #[cfg(feature = "backtrace")] return write!(f, "{}\n{}", self.msg, self.backtrace);
    }
}

impl Debug for JsonError {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        #[cfg(not(feature = "backtrace"))] return write!(f, "{{ msg: {} }}", self.msg);
        #[cfg(feature = "backtrace")] return write!(f, "{{ msg: {}, backtrace: {} }}", self.msg, self.backtrace);
    }
}

/// Searches for a key through the root JSON object of the
/// candidate string and returns a parsed value.
///
/// The searched value can only be a string or a float/integer,
/// objects, vectors and maps won't work. Furthermore, only keys
/// in the root JSON object will be compared to your search. If,
/// for example, you search for a key "a" which is only present
/// in a nested JSON object, the function will fail.
///
/// Returns a JsonError if the candidate string is not a correct
/// JSON string or if the function failed to find the key and
/// its associated value in the string.
pub fn root_search<'de, T>(key: &str, candidate: &'de str) -> Result<T, JsonError> where T: Deserialize<'de> {
    if key.is_empty() {
        return JsonError::err("The searched key can't be empty");
    }

    //get candidate slice starting at the first character of the value
    let value_begin = {
        let mut quoted_key = String::with_capacity(key.len() + 2);
        quoted_key.push('"');
        quoted_key.push_str(key);
        quoted_key.push('"');

        let mut iter = candidate.chars();
        let mut key_iter = quoted_key.chars();
        let mut prev_index = 0;
        let mut nesting_level = 0;
        let mut key_end = None;

        while key_end.is_none() {
            if nesting_level < 0 {
                return JsonError::err("Incorrectly formatted JSON string");
            }

            if let Some(next) = iter.next() {
                if next == '{' || next == '[' {
                    nesting_level += 1
                } else if next == '}' || next == ']' {
                    nesting_level -= 1
                }

                if nesting_level == 1 {
                    if let Some(key_next) = key_iter.next() {
                        if next != key_next {
                            key_iter = quoted_key.chars();
                        }
                    } else {
                        key_end = Some(prev_index + 1);
                        break;
                    }
                }

                prev_index += 1;
            } else {
                return JsonError::err("Could not find key in candidate");
            }
        }

        if key_end.is_none() {
            return JsonError::err("Could not find key in candidate");
        }

        match candidate[key_end.unwrap()..].find(|c: char| c.is_numeric() || c == '"') {
            Some(i) => &candidate[key_end.unwrap() + i..],
            None => return JsonError::err("Could not find value in candidate")
        }
    };

    let mut iter = value_begin.chars();
    let mut prev_index = 0;
    let mut nesting_level = 0;
    let mut value = None;

    while value.is_none() {
        if let Some(next) = iter.next() {
            if next == '{' || next == '[' {
                nesting_level += 1
            } else if next == '}' || next == ']' {
                nesting_level -= 1
            }

            if next == ',' || (nesting_level == -1 && next == '}') { //reached the end of the json value/string
                value = Some(&value_begin[..prev_index]);
            }

            prev_index += 1;
        } else {
            return JsonError::err("Unexpected end of string");
        }
    }

    if let Some(value) = value {
        return match serde_json::from_str(value) {
            Ok(value) => Ok(value),
            Err(e) => JsonError::err(format!("Failed to parse JSON: {}", e))
        };
    }

    JsonError::err("An error occurred while searching for key")
}

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

    #[test]
    fn test_root_search() {
        let simple_str = r#"{"key":"value"}"#;
        let simple_int = r#"{"key":432}"#;
        let simple_float = r#"{"key":5.56}"#;

        assert_eq!(root_search::<String>("key", simple_str).unwrap(), "value");
        assert_eq!(root_search::<u32>("key", simple_int).unwrap(), 432);
        assert_eq!(root_search::<f32>("key", simple_float).unwrap(), 5.56);
    }

    #[test]
    fn test_root_search_key_not_found() {
        let simple = r#"{"key":"value"}"#;

        assert_eq!(root_search::<String>("ke", simple).err().unwrap().msg, "Could not find key in candidate");
        assert_eq!(root_search::<String>("ey", simple).err().unwrap().msg, "Could not find key in candidate");
        assert_eq!(root_search::<String>("aaa", simple).err().unwrap().msg, "Could not find key in candidate");
        assert_eq!(root_search::<String>("value", simple).err().unwrap().msg, "Could not find key in candidate");
    }

    #[test]
    fn test_root_search_empty_key() {
        let simple = r#"{"key":"value"}"#;

        assert_eq!(root_search::<String>("", simple).err().unwrap().msg, "The searched key can't be empty");
    }

    #[test]
    fn test_root_search_nested_vec() {
        let contains_array = r#"{"array":["a","b","c"],"b":40}"#;

        assert_eq!(root_search::<u8>("b", contains_array).unwrap(), 40);
        assert_eq!(root_search::<String>("c", contains_array).err().unwrap().msg, "Could not find key in candidate");
    }

    #[test]
    fn test_root_search_nested_object() {
        let nested_safe_order = r#"{"a":700,"nested":{"a":700}}"#;
        let nested_risky_order = r#"{"nested":{"a":1000},"a":700}"#;

        assert_eq!(root_search::<u16>("a", nested_safe_order).unwrap(), 700);
        assert_eq!(root_search::<u16>("a", nested_risky_order).unwrap(), 700);
    }

    #[test]
    fn test_search_invalid_json() {
        let no_first_brace = r#""key":"value"}"#;
        let no_final_brace = r#"{"key":"value""#;
        let no_comma = r#"{"key":"value""other":5}"#;
        let no_key_quote = r#"{"key:"value","other":5}"#;
        let no_val_quote = r#"{"key":value","other":5}"#;
        let wrong_type = r#"{"int":"value"}"#;

        assert!(root_search::<String>("key", no_first_brace).is_err());
        assert!(root_search::<String>("key", no_final_brace).is_err());
        assert!(root_search::<String>("key", no_comma).is_err());
        assert!(root_search::<String>("key", no_key_quote).is_err());
        assert!(root_search::<String>("key", no_val_quote).is_err());
        assert!(root_search::<u32>("int", wrong_type).is_err());
    }

}

#[cfg(test)]
mod benchmarks {
    use super::*;
    use serde::Serialize;
    use test::Bencher;
    use std::collections::HashMap;

    #[derive(Serialize)]
    struct Something {
        somewhere: String,
        somehow: i32,
        someway: Vec<String>,
        somewhat: String,
        someday: HashMap<String, String>,
    }

    impl Something {
        fn create() -> Something {
            let mut vec = Vec::new();
            vec.push(String::from("Hello"));
            vec.push(String::from("world"));
            vec.push(String::from("!"));

            let mut map = HashMap::new();
            map.insert(String::from("Hello"), String::from("olleH"));
            map.insert(String::from("world"), String::from("dlrow"));
            map.insert(String::from("!"), String::from("!"));

            Something {
                somewhere: String::from("Not here"),
                somehow: -37218,
                someway: vec,
                somewhat: String::from("1936198231983251985"),
                someday: map,
            }
        }
    }

    #[derive(Serialize)]
    struct LongStr {
        first: String,
        second: String,
    }

    impl LongStr {
        fn create() -> LongStr {
            let mut first = String::new();
            let mut second = String::new();

            for _ in 0..500 {
                first.push_str("abc");
                second.push_str("d");
            }

            LongStr {
                first,
                second,
            }
        }
    }

    #[bench]
    fn bench_root_search_average(b: &mut Bencher) {
        let something = serde_json::to_string(&Something::create()).unwrap();

        b.iter(|| {
            root_search::<String>("somewhere", &something).unwrap();
        });
    }

    #[bench]
    fn bench_root_search_long_str(b: &mut Bencher) {
        let long_str = serde_json::to_string(&LongStr::create()).unwrap();

        b.iter(|| {
            root_search::<String>("second", &long_str).unwrap();
        });
    }
}