rust_json 0.1.6

A simple JSON serializer and deserializer
Documentation
use crate::{JsonElem, JsonParseErr};
use std::collections::HashMap;
use std::str::Chars;
use std::str::FromStr;

impl FromStr for JsonElem {
    type Err = JsonParseErr;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        json_parse(s)
    }
}

pub fn json_parse(json: &str) -> Result<JsonElem, JsonParseErr> {
    let mut json_trim = json.trim();
    let res = json_parse_value(&mut json_trim);
    if res.is_ok() && !json_trim.is_empty() {
        Err(JsonParseErr::RootNotSingular)
    } else {
        res
    }
}

fn json_parse_value(json: &mut &str) -> Result<JsonElem, JsonParseErr> {
    match json.chars().next() {
        Some('n') => json_parse_literal(json, "null", JsonElem::Null),
        Some('t') => json_parse_literal(json, "true", JsonElem::Bool(true)),
        Some('f') => json_parse_literal(json, "false", JsonElem::Bool(false)),
        Some('"') => json_parse_string(json),
        Some('[') => json_parse_array(json),
        Some('{') => json_parse_object(json),
        None => Err(JsonParseErr::ExpectValue),
        _ => json_parse_number(json),
    }
}

fn json_parse_literal(
    json: &mut &str,
    literal: &str,
    res: JsonElem,
) -> Result<JsonElem, JsonParseErr> {
    if let Some(new_json) = json.strip_prefix(literal) {
        *json = new_json;
        Ok(res)
    } else {
        Err(JsonParseErr::InvalidValue)
    }
}

fn json_parse_number(json: &mut &str) -> Result<JsonElem, JsonParseErr> {
    let mut iter = json.chars().peekable();
    if iter.peek() == Some(&'-') {
        iter.next();
    }

    if iter.peek() == Some(&'0') {
        iter.next();
    } else if is_digit_1_to_9(iter.peek()) {
        iter.next();
        while is_digit(iter.peek()) {
            iter.next();
        }
    } else {
        return Err(JsonParseErr::InvalidValue);
    }

    if iter.peek() == Some(&'.') {
        iter.next();
        if !is_digit(iter.peek()) {
            return Err(JsonParseErr::InvalidValue);
        }
        iter.next();
        while is_digit(iter.peek()) {
            iter.next();
        }
    }

    if iter.peek() == Some(&'e') || iter.peek() == Some(&'E') {
        iter.next();
        if iter.peek() == Some(&'+') || iter.peek() == Some(&'-') {
            iter.next();
        }
        if !is_digit(iter.peek()) {
            return Err(JsonParseErr::InvalidValue);
        }
        iter.next();
        while is_digit(iter.peek()) {
            iter.next();
        }
    }
    let len = json.chars().count() - iter.count();
    let end = json.char_indices().map(|(i, _)| i).nth(len - 1).unwrap();
    let number = &json[0..=end];
    *json = &json[(end + 1)..];
    Ok(JsonElem::Number(String::from(number).parse().unwrap()))
}

fn is_digit_1_to_9(ch: Option<&char>) -> bool {
    if let Some(ch) = ch {
        '1' <= *ch && *ch <= '9'
    } else {
        false
    }
}

fn is_digit(ch: Option<&char>) -> bool {
    if let Some(ch) = ch {
        '0' <= *ch && *ch <= '9'
    } else {
        false
    }
}

fn json_parse_string(json: &mut &str) -> Result<JsonElem, JsonParseErr> {
    let mut str_buf: Vec<char> = Vec::new();
    let mut chars = json.chars();
    chars.next();
    loop {
        match chars.next() {
            Some('"') => break,
            Some('\\') => match chars.next() {
                Some('"') => str_buf.push('"'),
                Some('\\') => str_buf.push('\\'),
                Some('/') => str_buf.push('/'),
                Some('b') => str_buf.push('\x08'),
                Some('f') => str_buf.push('\x0c'),
                Some('n') => str_buf.push('\n'),
                Some('r') => str_buf.push('\r'),
                Some('t') => str_buf.push('\t'),
                Some('u') => {
                    if let Some(res) = try_get_hex4(&mut chars) {
                        if (0xd800..=0xdbff).contains(&res) {
                            if chars.next() != Some('\\') {
                                return Err(JsonParseErr::InvalidUnicodeSurrogate);
                            }
                            if chars.next() != Some('u') {
                                return Err(JsonParseErr::InvalidUnicodeSurrogate);
                            }
                            if let Some(res2) = try_get_hex4(&mut chars) {
                                let u = (((res - 0xD800) << 10) | (res2 - 0xDC00)) + 0x10000;
                                if let Some(ch) = char::from_u32(u) {
                                    str_buf.push(ch)
                                } else {
                                    return Err(JsonParseErr::InvalidUnicodeSurrogate);
                                }
                            } else {
                                return Err(JsonParseErr::InvalidUnicodeSurrogate);
                            }
                        } else if let Some(ch) = char::from_u32(res) {
                            str_buf.push(ch)
                        } else {
                            return Err(JsonParseErr::InvalidUnicodeSurrogate);
                        }
                    } else {
                        return Err(JsonParseErr::InvalidUnicodeHex);
                    }
                }
                _ => return Err(JsonParseErr::InvalidStringEscape),
            },
            None => return Err(JsonParseErr::MissQuotationMark),
            Some(c) => {
                if (c as u32) < 0x20 {
                    return Err(JsonParseErr::InvalidStringChar);
                } else {
                    str_buf.push(c)
                }
            }
        }
    }
    *json = chars.as_str();
    Ok(JsonElem::Str(str_buf.into_iter().collect()))
}

fn hex_to_u32(h: char) -> u32 {
    match h {
        '0'..='9' => h as u32 - '0' as u32,
        'a'..='f' => h as u32 - 'a' as u32 + 10,
        'A'..='F' => h as u32 - 'A' as u32 + 10,
        _ => panic!("input is not a hex digit"),
    }
}

fn try_get_hex4(chars: &mut Chars) -> Option<u32> {
    let c1 = chars.next()?;
    let c2 = chars.next()?;
    let c3 = chars.next()?;
    let c4 = chars.next()?;
    if c1.is_ascii_hexdigit()
        && c2.is_ascii_hexdigit()
        && c3.is_ascii_hexdigit()
        && c4.is_ascii_hexdigit()
    {
        Some(
            (hex_to_u32(c1) << 12)
                + (hex_to_u32(c2) << 8)
                + (hex_to_u32(c3) << 4)
                + (hex_to_u32(c4)),
        )
    } else {
        None
    }
}

fn json_parse_array(json: &mut &str) -> Result<JsonElem, JsonParseErr> {
    let mut chars = json.chars();
    assert_eq!(Some('['), chars.next());
    let mut arr: Vec<JsonElem> = Vec::new();
    *json = chars.as_str().trim();
    if let Some(new_json) = json.strip_prefix(']') {
        *json = new_json;
        return Ok(JsonElem::Array(arr));
    }
    loop {
        let res = json_parse_value(json);
        if let Ok(elem) = res {
            arr.push(elem);
        } else {
            return res;
        }
        *json = json.trim();
        if let Some(new_json) = json.strip_prefix(',') {
            *json = new_json.trim();
        } else if let Some(new_json) = json.strip_prefix(']') {
            *json = new_json;
            return Ok(JsonElem::Array(arr));
        } else {
            return Err(JsonParseErr::ArrayMissCommaOrSquareBacket);
        }
    }
}

fn json_parse_object(json: &mut &str) -> Result<JsonElem, JsonParseErr> {
    let mut chars = json.chars();
    assert_eq!(Some('{'), chars.next());
    let mut obj = HashMap::new();
    *json = chars.as_str().trim();
    if let Some(new_json) = json.strip_prefix('}') {
        *json = new_json;
        return Ok(JsonElem::Object(obj));
    }
    loop {
        let res = json_parse_member(json);
        match res {
            Ok((key, elem)) => obj.insert(key, elem),
            Err(e) => return Err(e),
        };
        *json = json.trim();
        if let Some(new_json) = json.strip_prefix(',') {
            *json = new_json.trim();
        } else if let Some(new_json) = json.strip_prefix('}') {
            *json = new_json;
            return Ok(JsonElem::Object(obj));
        } else {
            return Err(JsonParseErr::ObjectMissCommaOrCurlyBacket);
        }
    }
}

fn json_parse_member(json: &mut &str) -> Result<(String, JsonElem), JsonParseErr> {
    if !json.starts_with('"') {
        return Err(JsonParseErr::ObjectMissKey);
    }
    let key;
    match json_parse_string(json) {
        Ok(JsonElem::Str(k)) => key = k,
        Err(e) => return Err(e),
        _ => panic!(
            "json_parse_string shouldn't return unexpected values other than JsonElem::Str and Err"
        ),
    };
    *json = json.trim();
    match json.strip_prefix(':') {
        Some(new_json) => *json = new_json.trim(),
        None => return Err(JsonParseErr::ObjectMissColon),
    };
    match json_parse_value(json) {
        Ok(elem) => Ok((key, elem)),
        Err(e) => Err(e),
    }
}