tiny_serde/
deserialize.rs

1use std::{
2    collections::HashMap,
3    iter::Peekable,
4    str::{CharIndices, FromStr},
5};
6
7use crate::{error::SerDeJsonError, Number, Value};
8
9enum Ident {
10    True,
11    False,
12    Null,
13}
14
15pub trait Deserialize {
16    fn deserialize(value: Value) -> Result<Self, SerDeJsonError>
17    where
18        Self: Sized;
19}
20
21impl Deserialize for Value {
22    fn deserialize(value: Value) -> Result<Self, SerDeJsonError>
23    where
24        Self: Sized,
25    {
26        Ok(value)
27    }
28}
29
30pub fn from_str<T: Deserialize>(value: &str) -> Result<T, SerDeJsonError> {
31    let value = Value::from_str(value)?;
32    T::deserialize(value)
33}
34
35impl FromStr for Value {
36    type Err = SerDeJsonError;
37
38    fn from_str(value: &str) -> Result<Self, Self::Err> {
39        let value = value.trim();
40        let value = value.char_indices().peekable();
41        let mut tokens = JsonParser::new(value);
42        let value = tokens.parse()?;
43        Ok(value)
44    }
45}
46
47struct JsonParser<'a> {
48    reader: Peekable<CharIndices<'a>>,
49    index: usize,
50}
51
52impl<'a> JsonParser<'a> {
53    pub fn new(reader: Peekable<CharIndices<'a>>) -> Self {
54        Self { reader, index: 0 }
55    }
56
57    ///Parse the string into a `Value`
58    pub fn parse(&mut self) -> Result<Value, SerDeJsonError> {
59        let value = match self.skip_whitespaces()? {
60            '{' => self.parse_object()?,
61            '[' => self.parse_array()?,
62            _ => return Err(SerDeJsonError::InvalidJson { index: self.index }),
63        };
64
65        match self.skip_whitespaces() {
66            Ok('\0') | Err(..) => (),
67            _ => return Err(SerDeJsonError::InvalidJson { index: self.index }),
68        }
69
70        Ok(value)
71    }
72
73    ///Consume all the whitespaces and return the next character without consuming it
74    ///
75    ///Return an error if the stream of characters ends before finding a non-whitespace character
76    pub fn skip_whitespaces(&mut self) -> Result<char, SerDeJsonError> {
77        loop {
78            let Some((i, c)) = self.reader.peek() else {
79                return Err(SerDeJsonError::UnexpectedEnd { index: self.index });
80            };
81            self.index = *i;
82            if c.is_ascii_whitespace() {
83                self.reader.next();
84                continue;
85            }
86            break Ok(*c);
87        }
88    }
89
90    ///Consume the next object and return it as a `Value`
91    pub fn parse_object(&mut self) -> Result<Value, SerDeJsonError> {
92        self.reader.next();
93        let mut final_value = HashMap::new();
94        loop {
95            match self.skip_whitespaces()? {
96                '"' => (),
97                _ => return Err(SerDeJsonError::InvalidJson { index: self.index }),
98            }
99            let key = self.parse_string()?;
100            match self.skip_whitespaces()? {
101                ':' => {
102                    self.reader.next();
103                }
104                _ => return Err(SerDeJsonError::InvalidJson { index: self.index }),
105            }
106
107            let c = self.skip_whitespaces()?;
108            let value = self.parse_value(c)?;
109
110            if final_value.insert(key.clone(), value).is_some() {
111                return Err(SerDeJsonError::DupplicateKey {
112                    key,
113                    index: self.index,
114                });
115            }
116
117            match self.skip_whitespaces()? {
118                ',' => {
119                    self.reader.next();
120                    continue;
121                }
122                '}' => break,
123                _ => return Err(SerDeJsonError::InvalidJson { index: self.index }),
124            }
125        }
126
127        self.reader.next();
128
129        Ok(Value::Object(final_value))
130    }
131
132    ///Consume the next array and return it as a `Value`
133    pub fn parse_array(&mut self) -> Result<Value, SerDeJsonError> {
134        self.reader.next();
135
136        let mut final_value = Vec::new();
137
138        loop {
139            let c = self.skip_whitespaces()?;
140            let value = self.parse_value(c)?;
141
142            final_value.push(value);
143
144            match self.skip_whitespaces()? {
145                ',' => {
146                    self.reader.next();
147                    continue;
148                }
149                ']' => break,
150                _ => return Err(SerDeJsonError::InvalidJson { index: self.index }),
151            }
152        }
153
154        self.reader.next();
155
156        Ok(Value::Array(final_value))
157    }
158
159    ///Consume the next value and return it
160    pub fn parse_value(&mut self, next_char: char) -> Result<Value, SerDeJsonError> {
161        let value = match next_char {
162            '"' => Value::String(self.parse_string()?),
163            '-' | '0'..='9' => self.parse_number()?,
164            '{' => self.parse_object()?,
165            '[' => self.parse_array()?,
166            't' => self.parse_ident(Ident::True)?,
167            'f' => self.parse_ident(Ident::False)?,
168            'n' => self.parse_ident(Ident::Null)?,
169            _ => return Err(SerDeJsonError::InvalidJson { index: self.index }),
170        };
171
172        Ok(value)
173    }
174
175    pub fn parse_number(&mut self) -> Result<Value, SerDeJsonError> {
176        //Get the first character of the number
177        let Some((i, c)) = self.reader.peek() else {
178            return Err(SerDeJsonError::UnexpectedEnd { index: self.index });
179        };
180
181        self.index = *i;
182
183        //Check if the number is negative, if so, get the next character
184        let (c, is_positive) = if *c == '-' {
185            self.reader.next();
186            let Some((i, c)) = self.reader.peek() else {
187                return Err(SerDeJsonError::UnexpectedEnd { index: self.index });
188            };
189            self.index = *i;
190            (c, false)
191        } else {
192            (c, true)
193        };
194
195        //Get if the integer part of the number
196        let full_number = match c {
197            '0' => {
198                self.reader.next();
199                '0'.to_string()
200            }
201            '1'..='9' => self.parse_integer()?,
202            _ => return Err(SerDeJsonError::InvalidJson { index: self.index }),
203        };
204
205        //Handle decimal part, if some
206        let Some((i, c)) = self.reader.peek() else {
207            return Err(SerDeJsonError::UnexpectedEnd { index: self.index });
208        };
209        self.index = *i;
210        let (full_number, is_decimal) = match *c {
211            '.' => {
212                self.reader.next();
213                (full_number + &self.parse_integer()?, true)
214            }
215            _ => (full_number, false),
216        };
217
218        //Handle exponent part, if some
219        let Some((i, c)) = self.reader.peek() else {
220            return Err(SerDeJsonError::UnexpectedEnd { index: self.index });
221        };
222        self.index = *i;
223
224        let final_number = match *c {
225            'e' | 'E' => {
226                let full_number: f64 = match full_number.parse() {
227                    Ok(full_number) => full_number,
228                    Err(e) => {
229                        return Err(SerDeJsonError::NumberError {
230                            number: full_number,
231                            index: self.index,
232                            error: e.to_string(),
233                        })
234                    }
235                };
236                self.reader.next();
237                let Some((i, c)) = self.reader.peek() else {
238                    return Err(SerDeJsonError::UnexpectedEnd { index: self.index });
239                };
240                self.index = *i;
241                let mut full_exponent_part = String::new();
242                let c = *c;
243                if c == '+' || c == '-' {
244                    self.reader.next();
245                    full_exponent_part.push(c);
246                }
247                let full_exponent_part = full_exponent_part + &self.parse_integer()?;
248                let full_exponent_part: i32 = match full_exponent_part.parse() {
249                    Ok(full_exponent_part) => full_exponent_part,
250                    Err(e) => {
251                        return Err(SerDeJsonError::NumberError {
252                            number: full_exponent_part,
253                            index: self.index,
254                            error: e.to_string(),
255                        })
256                    }
257                };
258                let final_number = full_number * 10f64.powi(full_exponent_part);
259                Number::Decimal(final_number)
260            }
261            _ => {
262                if is_decimal {
263                    let nb: f64 = match full_number.parse() {
264                        Ok(nb) => nb,
265                        Err(e) => {
266                            return Err(SerDeJsonError::NumberError {
267                                number: full_number,
268                                index: self.index,
269                                error: e.to_string(),
270                            })
271                        }
272                    };
273                    Number::Decimal(nb)
274                } else {
275                    if is_positive {
276                        let nb: u128 = match full_number.parse() {
277                            Ok(nb) => nb,
278                            Err(e) => {
279                                return Err(SerDeJsonError::NumberError {
280                                    number: full_number,
281                                    index: self.index,
282                                    error: e.to_string(),
283                                })
284                            }
285                        };
286                        Number::Integer(nb)
287                    } else {
288                        let nb: i128 = match full_number.parse() {
289                            Ok(nb) => nb,
290                            Err(e) => {
291                                return Err(SerDeJsonError::NumberError {
292                                    number: full_number,
293                                    index: self.index,
294                                    error: e.to_string(),
295                                })
296                            }
297                        };
298                        Number::SignedInteger(nb)
299                    }
300                }
301            }
302        };
303
304        Ok(Value::Number(final_number))
305    }
306
307    ///Parse an integer (this function parses until a non-digit character is found)
308    ///
309    ///Return the number as a String
310    fn parse_integer(&mut self) -> Result<String, SerDeJsonError> {
311        let mut integer_part = String::new();
312
313        loop {
314            let Some((i, c)) = self.reader.peek() else {
315                return Err(SerDeJsonError::UnexpectedEnd { index: self.index });
316            };
317            self.index = *i;
318            let c = *c;
319            match c {
320                '0'..='9' => {
321                    self.reader.next();
322                    integer_part.push(c);
323                }
324                _ => break,
325            }
326        }
327
328        if integer_part.is_empty() {
329            return Err(SerDeJsonError::InvalidJson { index: self.index });
330        }
331
332        Ok(integer_part)
333    }
334
335    ///Consume the next string and return it as a Value
336    pub fn parse_string(&mut self) -> Result<String, SerDeJsonError> {
337        self.reader.next();
338
339        let mut chars: Vec<char> = Vec::new();
340
341        loop {
342            let Some((i, c)) = self.reader.next() else {
343                return Err(SerDeJsonError::InvalidJson { index: self.index });
344            };
345            self.index = i;
346            match c {
347                '"' => break,
348                '\\' => {
349                    let Some((i, c)) = self.reader.next() else {
350                        return Err(SerDeJsonError::InvalidJson { index: self.index });
351                    };
352                    self.index = i;
353                    let c = match c {
354                        '"' => '\"',
355                        '\\' => '\\',
356                        'n' => '\n',
357                        'r' => '\r',
358                        't' => '\t',
359                        'u' => {
360                            let mut hex_digits = String::new();
361                            for _ in 0..4 {
362                                let Some((i, c)) = self.reader.next() else {
363                                    return Err(SerDeJsonError::InvalidJson { index: self.index });
364                                };
365                                self.index = i;
366                                hex_digits.push(c);
367                            }
368                            let code_point = u32::from_str_radix(&hex_digits, 16).unwrap();
369                            match char::from_u32(code_point) {
370                                Some(c) => c,
371                                None => {
372                                    return Err(SerDeJsonError::InvalidJson { index: self.index })
373                                }
374                            }
375                        }
376                        _ => return Err(SerDeJsonError::InvalidJson { index: self.index }),
377                    };
378                    chars.push(c);
379                }
380                c => chars.push(c),
381            }
382        }
383
384        Ok(String::from_iter(chars))
385    }
386
387    ///Parse `true`, `false` and `null` and return it as a Value
388    pub fn parse_ident(&mut self, ident: Ident) -> Result<Value, SerDeJsonError> {
389        let (value, ident) = match ident {
390            Ident::True => (Value::Boolean(true), "true"),
391            Ident::False => (Value::Boolean(false), "false"),
392            Ident::Null => (Value::Null, "null"),
393        };
394
395        for c_in_pattern in ident.chars() {
396            let Some((i, c)) = self.reader.peek() else {
397                return Err(SerDeJsonError::UnexpectedEnd { index: self.index });
398            };
399            self.index = *i;
400            if *c == ',' || c.is_ascii_whitespace() {
401                break;
402            }
403            if *c != c_in_pattern {
404                return Err(SerDeJsonError::InvalidJson { index: self.index });
405            }
406            self.reader.next();
407        }
408
409        Ok(value)
410    }
411}
412
413#[cfg(test)]
414mod tests {
415    use crate::{error::SerDeJsonError, Value};
416
417    #[test]
418    fn test() {
419        let truc = r#"[
420  {
421    "_id": "666895f3d593468af0ab2235",
422    "index": 0,
423    "guid": "d1f9ebea-c276-4617-90aa-3cd30edc3ae9",
424    "isActive": true,
425    "balance": "$2,310.49",
426    "picture": "http://placehold.it/32x32",
427    "age": 29,
428    "eyeColor": "green",
429    "name": "Kidd Sykes",
430    "gender": "male",
431    "company": "TRASOLA",
432    "email": "kiddsykes@trasola.com",
433    "phone": "+1 (823) 468-2410",
434    "address": "778 Alton Place, Allamuchy, Delaware, 6398",
435    "about": "Labore consequat laborum nisi deserunt nisi nisi dolore fugiat commodo voluptate minim est qui dolore. Aliqua elit est nostrud eu ad excepteur. Esse incididunt laboris enim culpa commodo.\r\n",
436    "registered": "2022-08-31T07:11:46 -02:00",
437    "latitude": 29.07863,
438    "longitude": -118.387919,
439    "tags": [
440      "nulla",
441      "voluptate",
442      "ut",
443      "voluptate",
444      "sunt",
445      "labore",
446      "sunt"
447    ],
448    "friends": [
449      {
450        "id": 0,
451        "name": "Rosario Britt"
452      },
453      {
454        "id": 1,
455        "name": "Maribel Fowler"
456      },
457      {
458        "id": 2,
459        "name": "Jenkins Church"
460      }
461    ],
462    "greeting": "Hello, Kidd Sykes! You have 9 unread messages.",
463    "favoriteFruit": "strawberry"
464  },
465  {
466    "_id": "666895f371fbd3e96171e666",
467    "index": 1,
468    "guid": "da698537-f194-43b5-b270-bceb89a793dc",
469    "isActive": false,
470    "balance": "$3,446.47",
471    "picture": "http://placehold.it/32x32",
472    "age": 34,
473    "eyeColor": "brown",
474    "name": "Tyler Frank",
475    "gender": "male",
476    "company": "BITENDREX",
477    "email": "tylerfrank@bitendrex.com",
478    "phone": "+1 (835) 435-2740",
479    "address": "548 Bergen Street, Downsville, American Samoa, 2917",
480    "about": "Occaecat et Lorem magna ea. Sit in ullamco commodo qui et est in duis mollit sunt exercitation aute aliquip. Occaecat laboris incididunt anim esse ea id magna. Cupidatat eu do enim cupidatat esse amet elit in aliqua. Enim duis cupidatat proident eu adipisicing amet tempor non ullamco aute nostrud excepteur cupidatat.\r\n",
481    "registered": "2022-07-18T09:13:24 -02:00",
482    "latitude": -31.405638,
483    "longitude": 113.128241,
484    "tags": [
485      "officia",
486      "qui",
487      "eiusmod",
488      "magna",
489      "velit",
490      "commodo",
491      "officia"
492    ],
493    "friends": [
494      {
495        "id": 0,
496        "name": "Saundra Grant"
497      },
498      {
499        "id": 1,
500        "name": "Burt Craft"
501      },
502      {
503        "id": 2,
504        "name": "Vinson Vega"
505      }
506    ],
507    "greeting": "Hello, Tyler Frank! You have 2 unread messages.",
508    "favoriteFruit": "banana"
509  }
510]"#;
511
512        let value: Value = match crate::from_str(truc) {
513            Ok(value) => value,
514            Err(e) => match e {
515                SerDeJsonError::InvalidJson { index }
516                | SerDeJsonError::UnexpectedEnd { index }
517                | SerDeJsonError::DupplicateKey { index, .. }
518                | SerDeJsonError::NumberError { index, .. } => {
519                    let begin = index - 10;
520                    let end = index + 10;
521                    let char = truc.get(index..=index).unwrap();
522                    let truc_extract = truc.get(begin..=end).unwrap();
523                    panic!(
524                        "From index {begin} to index {end} : `{}`, character `{char}` : {e:?}",
525                        truc_extract
526                    );
527                }
528                _ => unimplemented!(),
529            },
530        };
531
532        println!("{value:?}\n\n\n{}", value.to_string());
533    }
534}