#![allow(clippy::identity_op)]

use std::{
    char::from_u32 as char_from_u32,
    str::{self, from_utf8, FromStr, Utf8Error},
};

use unicode_ident::{is_xid_continue, is_xid_start};

use crate::{
    error::{Error, Position, Result, SpannedError, SpannedResult},
    extensions::Extensions,
    value::Number,
};

const fn is_int_char(c: char) -> bool {
    c.is_ascii_hexdigit() || c == '_'
}

const fn is_float_char(c: char) -> bool {
    c.is_ascii_digit() || matches!(c, 'e' | 'E' | '.' | '+' | '-' | '_')
}

pub fn is_ident_first_char(c: char) -> bool {
    c == '_' || is_xid_start(c)
}

pub fn is_ident_raw_char(c: char) -> bool {
    matches!(c, '.' | '+' | '-') | is_xid_continue(c)
}

pub const fn is_whitespace_char(c: char) -> bool {
    matches!(
        c,
        ' ' | '\t'
            | '\n'
            | '\r'
            | '\x0B'
            | '\x0C'
            | '\u{85}'
            | '\u{200E}'
            | '\u{200F}'
            | '\u{2028}'
            | '\u{2029}'
    )
}

#[cfg(feature = "integer128")]
pub(crate) type LargeUInt = u128;
#[cfg(not(feature = "integer128"))]
pub(crate) type LargeUInt = u64;
#[cfg(feature = "integer128")]
pub(crate) type LargeSInt = i128;
#[cfg(not(feature = "integer128"))]
pub(crate) type LargeSInt = i64;

pub struct Parser<'a> {
    /// Bits set according to the [`Extensions`] enum.
    pub exts: Extensions,
    src: &'a str,
    cursor: ParserCursor,
}

#[derive(Copy, Clone)] // GRCOV_EXCL_LINE
pub struct ParserCursor {
    cursor: usize,
    pre_ws_cursor: usize,
    last_ws_len: usize,
}

const WS_CURSOR_UNCLOSED_LINE: usize = usize::MAX;

impl PartialEq for ParserCursor {
    fn eq(&self, other: &Self) -> bool {
        self.cursor == other.cursor
    }
}

impl PartialOrd for ParserCursor {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.cursor.partial_cmp(&other.cursor)
    }
}

/// constructor and parsing utilities
impl<'a> Parser<'a> {
    pub fn new(src: &'a str) -> SpannedResult<Self> {
        let mut parser = Parser {
            exts: Extensions::empty(),
            src,
            cursor: ParserCursor {
                cursor: 0,
                pre_ws_cursor: 0,
                last_ws_len: 0,
            },
        };

        parser.skip_ws().map_err(|e| parser.span_error(e))?;

        // Loop over all extensions attributes
        loop {
            let attribute = parser.extensions().map_err(|e| parser.span_error(e))?;

            if attribute.is_empty() {
                break;
            }

            parser.exts |= attribute;
            parser.skip_ws().map_err(|e| parser.span_error(e))?;
        }

        Ok(parser)
    }

    fn set_cursor(&mut self, cursor: ParserCursor) {
        self.cursor = cursor;
    }

    pub fn span_error(&self, code: Error) -> SpannedError {
        SpannedError {
            code,
            position: Position::from_src_end(&self.src[..self.cursor.cursor]),
        }
    }

    pub fn advance_bytes(&mut self, bytes: usize) {
        self.cursor.cursor += bytes;
    }

    pub fn next_char(&mut self) -> Result<char> {
        let c = self.peek_char_or_eof()?;
        self.cursor.cursor += c.len_utf8();
        Ok(c)
    }

    pub fn skip_next_char(&mut self) {
        std::mem::drop(self.next_char());
    }

    pub fn peek_char(&self) -> Option<char> {
        self.src().chars().next()
    }

    pub fn peek_char_or_eof(&self) -> Result<char> {
        self.peek_char().ok_or(Error::Eof)
    }

    pub fn check_char(&self, c: char) -> bool {
        self.src().starts_with(c)
    }

    pub fn check_str(&self, s: &str) -> bool {
        self.src().starts_with(s)
    }

    pub fn src(&self) -> &'a str {
        &self.src[self.cursor.cursor..]
    }

    pub fn pre_ws_src(&self) -> &'a str {
        &self.src[self.cursor.pre_ws_cursor..]
    }

    pub fn consume_str(&mut self, s: &str) -> bool {
        if self.check_str(s) {
            self.advance_bytes(s.len());

            true
        } else {
            false
        }
    }

    pub fn consume_char(&mut self, c: char) -> bool {
        if self.check_char(c) {
            self.advance_bytes(c.len_utf8());

            true
        } else {
            false
        }
    }

    fn consume_all(&mut self, all: &[&str]) -> Result<bool> {
        all.iter()
            .map(|elem| {
                if self.consume_str(elem) {
                    self.skip_ws()?;

                    Ok(true)
                } else {
                    Ok(false)
                }
            })
            .try_fold(true, |acc, x| x.map(|x| x && acc))
    }

    pub fn expect_char(&mut self, expected: char, error: Error) -> Result<()> {
        if self.consume_char(expected) {
            Ok(())
        } else {
            Err(error)
        }
    }

    #[must_use]
    pub fn next_chars_while_len(&self, condition: fn(char) -> bool) -> usize {
        self.next_chars_while_from_len(0, condition)
    }

    #[must_use]
    pub fn next_chars_while_from_len(&self, from: usize, condition: fn(char) -> bool) -> usize {
        self.src()[from..]
            .find(|c| !condition(c))
            .unwrap_or(self.src().len() - from)
    }
}

/// actual parsing of ron tokens
impl<'a> Parser<'a> {
    fn parse_integer_digits<T: Num>(
        &mut self,
        s: &str,
        base: u8,
        f: fn(&mut T, u8) -> bool,
    ) -> Result<T> {
        let mut num_acc = T::from_u8(0);

        for (i, c) in s.char_indices() {
            if c == '_' {
                continue;
            }

            if num_acc.checked_mul_ext(base) {
                self.advance_bytes(s.len());
                return Err(Error::IntegerOutOfBounds);
            }

            let digit = Self::decode_hex(c)?;

            if digit >= base {
                self.advance_bytes(i);
                return Err(Error::InvalidIntegerDigit { digit: c, base });
            }

            if f(&mut num_acc, digit) {
                self.advance_bytes(s.len());
                return Err(Error::IntegerOutOfBounds);
            }
        }

        self.advance_bytes(s.len());

        Ok(num_acc)
    }

    fn parse_integer<T: Num>(&mut self, sign: i8) -> Result<T> {
        let base = match () {
            () if self.consume_str("0b") => 2,
            () if self.consume_str("0o") => 8,
            () if self.consume_str("0x") => 16,
            () => 10,
        };

        let num_bytes = self.next_chars_while_len(is_int_char);

        if num_bytes == 0 {
            return Err(Error::ExpectedInteger);
        }

        if self.check_char('_') {
            return Err(Error::UnderscoreAtBeginning);
        }

        let s = &self.src()[..num_bytes];

        if sign > 0 {
            self.parse_integer_digits(s, base, T::checked_add_ext)
        } else {
            self.parse_integer_digits(s, base, T::checked_sub_ext)
        }
    }

    #[allow(clippy::too_many_lines)]
    pub fn integer<T: Integer>(&mut self) -> Result<T> {
        let src_backup = self.src();

        let is_negative = match self.peek_char_or_eof()? {
            '+' => {
                self.skip_next_char();
                false
            }
            '-' => {
                self.skip_next_char();
                true
            }
            'b' if self.consume_str("b'") => {
                // Parse a byte literal
                let byte = match self.next_char()? {
                    '\\' => match self.parse_escape(EscapeEncoding::Binary, true)? {
                        // we know that this byte is an ASCII character
                        EscapeCharacter::Ascii(b) => b,
                        EscapeCharacter::Utf8(_) => {
                            return Err(Error::InvalidEscape(
                                "Unexpected Unicode escape in byte literal",
                            ))
                        }
                    },
                    b if b.is_ascii() => b as u8,
                    _ => return Err(Error::ExpectedByteLiteral),
                };

                if !self.consume_char('\'') {
                    return Err(Error::ExpectedByteLiteral);
                }

                let bytes_ron = &src_backup[..src_backup.len() - self.src().len()];

                return T::try_from_parsed_integer(ParsedInteger::U8(byte), bytes_ron);
            }
            _ => false,
        };
        let sign = if is_negative { -1 } else { 1 };

        let num_bytes = self.next_chars_while_len(is_int_char);

        if self.src()[num_bytes..].starts_with(['i', 'u']) {
            let int_cursor = self.cursor;
            self.advance_bytes(num_bytes);

            #[allow(clippy::never_loop)]
            loop {
                let (res, suffix_bytes) = if self.consume_ident("i8") {
                    let suffix_bytes = self.src();
                    self.set_cursor(int_cursor);
                    (
                        self.parse_integer::<i8>(sign).map(ParsedInteger::I8),
                        suffix_bytes,
                    )
                } else if self.consume_ident("i16") {
                    let suffix_bytes = self.src();
                    self.set_cursor(int_cursor);
                    (
                        self.parse_integer::<i16>(sign).map(ParsedInteger::I16),
                        suffix_bytes,
                    )
                } else if self.consume_ident("i32") {
                    let suffix_bytes = self.src();
                    self.set_cursor(int_cursor);
                    (
                        self.parse_integer::<i32>(sign).map(ParsedInteger::I32),
                        suffix_bytes,
                    )
                } else if self.consume_ident("i64") {
                    let suffix_bytes = self.src();
                    self.set_cursor(int_cursor);
                    (
                        self.parse_integer::<i64>(sign).map(ParsedInteger::I64),
                        suffix_bytes,
                    )
                } else if self.consume_ident("u8") {
                    let suffix_bytes = self.src();
                    self.set_cursor(int_cursor);
                    (
                        self.parse_integer::<u8>(sign).map(ParsedInteger::U8),
                        suffix_bytes,
                    )
                } else if self.consume_ident("u16") {
                    let suffix_bytes = self.src();
                    self.set_cursor(int_cursor);
                    (
                        self.parse_integer::<u16>(sign).map(ParsedInteger::U16),
                        suffix_bytes,
                    )
                } else if self.consume_ident("u32") {
                    let suffix_bytes = self.src();
                    self.set_cursor(int_cursor);
                    (
                        self.parse_integer::<u32>(sign).map(ParsedInteger::U32),
                        suffix_bytes,
                    )
                } else if self.consume_ident("u64") {
                    let suffix_bytes = self.src();
                    self.set_cursor(int_cursor);
                    (
                        self.parse_integer::<u64>(sign).map(ParsedInteger::U64),
                        suffix_bytes,
                    )
                } else {
                    #[cfg(feature = "integer128")]
                    if self.consume_ident("i128") {
                        let suffix_bytes = self.src();
                        self.set_cursor(int_cursor);
                        (
                            self.parse_integer::<i128>(sign).map(ParsedInteger::I128),
                            suffix_bytes,
                        )
                    } else if self.consume_ident("u128") {
                        let suffix_bytes = self.src();
                        self.set_cursor(int_cursor);
                        (
                            self.parse_integer::<u128>(sign).map(ParsedInteger::U128),
                            suffix_bytes,
                        )
                    } else {
                        break;
                    }
                    #[cfg(not(feature = "integer128"))]
                    {
                        break;
                    }
                };

                if !matches!(
                    &res,
                    Err(Error::UnderscoreAtBeginning | Error::InvalidIntegerDigit { .. })
                ) {
                    // Advance past the number suffix
                    self.skip_identifier();
                }

                let integer_ron = &src_backup[..src_backup.len() - suffix_bytes.len()];

                return res.and_then(|parsed| T::try_from_parsed_integer(parsed, integer_ron));
            }

            self.set_cursor(int_cursor);
        }

        T::parse(self, sign)
    }

    pub fn any_number(&mut self) -> Result<Number> {
        if self.next_bytes_is_float() {
            return match self.float::<ParsedFloat>()? {
                ParsedFloat::F32(v) => Ok(Number::F32(v.into())),
                ParsedFloat::F64(v) => Ok(Number::F64(v.into())),
            };
        }

        let backup_cursor = self.cursor;

        let (integer_err, integer_cursor) = match self.integer::<ParsedInteger>() {
            Ok(integer) => {
                return match integer {
                    ParsedInteger::I8(v) => Ok(Number::I8(v)),
                    ParsedInteger::I16(v) => Ok(Number::I16(v)),
                    ParsedInteger::I32(v) => Ok(Number::I32(v)),
                    ParsedInteger::I64(v) => Ok(Number::I64(v)),
                    #[cfg(feature = "integer128")]
                    ParsedInteger::I128(v) => Ok(Number::I128(v)),
                    ParsedInteger::U8(v) => Ok(Number::U8(v)),
                    ParsedInteger::U16(v) => Ok(Number::U16(v)),
                    ParsedInteger::U32(v) => Ok(Number::U32(v)),
                    ParsedInteger::U64(v) => Ok(Number::U64(v)),
                    #[cfg(feature = "integer128")]
                    ParsedInteger::U128(v) => Ok(Number::U128(v)),
                }
            }
            Err(err) => (err, self.cursor),
        };

        self.set_cursor(backup_cursor);

        // Fall-back to parse an out-of-range integer as a float
        match self.float::<ParsedFloat>() {
            Ok(ParsedFloat::F32(v)) if self.cursor >= integer_cursor => Ok(Number::F32(v.into())),
            Ok(ParsedFloat::F64(v)) if self.cursor >= integer_cursor => Ok(Number::F64(v.into())),
            _ => {
                // Return the more precise integer error
                self.set_cursor(integer_cursor);
                Err(integer_err)
            }
        }
    }

    pub fn bool(&mut self) -> Result<bool> {
        if self.consume_ident("true") {
            Ok(true)
        } else if self.consume_ident("false") {
            Ok(false)
        } else {
            Err(Error::ExpectedBoolean)
        }
    }

    pub fn char(&mut self) -> Result<char> {
        self.expect_char('\'', Error::ExpectedChar)?;

        let c = self.next_char()?;

        let c = if c == '\\' {
            match self.parse_escape(EscapeEncoding::Utf8, true)? {
                // we know that this byte is an ASCII character
                EscapeCharacter::Ascii(b) => char::from(b),
                EscapeCharacter::Utf8(c) => c,
            }
        } else {
            c
        };

        self.expect_char('\'', Error::ExpectedChar)?;

        Ok(c)
    }

    pub fn comma(&mut self) -> Result<bool> {
        self.skip_ws()?;

        if self.consume_char(',') {
            self.skip_ws()?;

            Ok(true)
        } else {
            Ok(false)
        }
    }

    /// Only returns true if the char after `ident` cannot belong
    /// to an identifier.
    pub fn check_ident(&mut self, ident: &str) -> bool {
        self.check_str(ident) && !self.check_ident_other_char(ident.len())
    }

    fn check_ident_other_char(&self, index: usize) -> bool {
        self.src()[index..]
            .chars()
            .next()
            .map_or(false, is_xid_continue)
    }

    /// Check which type of struct we are currently parsing. The parsing state
    ///  is only changed in case of an error, to provide a better position.
    ///
    /// [`NewtypeMode::NoParensMeanUnit`] detects (tuple) structs by a leading
    ///  opening bracket and reports a unit struct otherwise.
    /// [`NewtypeMode::InsideNewtype`] skips an initial check for unit structs,
    ///  and means that any leading opening bracket is not considered to open
    ///  a (tuple) struct but to be part of the structs inner contents.
    ///
    /// [`TupleMode::ImpreciseTupleOrNewtype`] only performs a cheap, O(1),
    ///  single-identifier lookahead check to distinguish tuple structs from
    ///  non-tuple structs.
    /// [`TupleMode::DifferentiateNewtype`] performs an expensive, O(N), look-
    ///  ahead over the entire next value tree, which can span the entirety of
    ///  the remaining document in the worst case.
    pub fn check_struct_type(
        &mut self,
        newtype: NewtypeMode,
        tuple: TupleMode,
    ) -> Result<StructType> {
        fn check_struct_type_inner(
            parser: &mut Parser,
            newtype: NewtypeMode,
            tuple: TupleMode,
        ) -> Result<StructType> {
            if matches!(newtype, NewtypeMode::NoParensMeanUnit) && !parser.consume_char('(') {
                return Ok(StructType::Unit);
            }

            parser.skip_ws()?;

            // Check for `Ident()`, which could be
            // - a zero-field struct or tuple (variant)
            // - an unwrapped newtype around a unit
            if matches!(newtype, NewtypeMode::NoParensMeanUnit) && parser.check_char(')') {
                return Ok(StructType::EmptyTuple);
            }

            if parser.skip_identifier().is_some() {
                parser.skip_ws()?;

                match parser.peek_char() {
                    // Definitely a struct with named fields
                    Some(':') => return Ok(StructType::Named),
                    // Definitely a tuple-like struct with fields
                    Some(',') => {
                        parser.skip_next_char();
                        parser.skip_ws()?;
                        if parser.check_char(')') {
                            // A one-element tuple could be a newtype
                            return Ok(StructType::NewtypeTuple);
                        }
                        // Definitely a tuple struct with more than one field
                        return Ok(StructType::NonNewtypeTuple);
                    }
                    // Either a newtype or a tuple struct
                    Some(')') => return Ok(StructType::NewtypeTuple),
                    // Something else, let's investigate further
                    Some(_) | None => (),
                };
            }

            if matches!(tuple, TupleMode::ImpreciseTupleOrNewtype) {
                return Ok(StructType::AnyTuple);
            }

            let mut braces = 1_usize;
            let mut more_than_one = false;

            // Skip ahead to see if the value is followed by another value
            while braces > 0 {
                // Skip spurious braces in comments, strings, and characters
                parser.skip_ws()?;
                let cursor_backup = parser.cursor;
                if parser.char().is_err() {
                    parser.set_cursor(cursor_backup);
                }
                let cursor_backup = parser.cursor;
                match parser.string() {
                    Ok(_) => (),
                    // prevent quadratic complexity backtracking for unterminated string
                    Err(err @ (Error::ExpectedStringEnd | Error::Eof)) => return Err(err),
                    Err(_) => parser.set_cursor(cursor_backup),
                }
                let cursor_backup = parser.cursor;
                // we have already checked for strings, which subsume base64 byte strings
                match parser.byte_string_no_base64() {
                    Ok(_) => (),
                    // prevent quadratic complexity backtracking for unterminated byte string
                    Err(err @ (Error::ExpectedStringEnd | Error::Eof)) => return Err(err),
                    Err(_) => parser.set_cursor(cursor_backup),
                }

                let c = parser.next_char()?;
                if matches!(c, '(' | '[' | '{') {
                    braces += 1;
                } else if matches!(c, ')' | ']' | '}') {
                    braces -= 1;
                } else if c == ',' && braces == 1 {
                    parser.skip_ws()?;
                    more_than_one = !parser.check_char(')');
                    break;
                }
            }

            if more_than_one {
                Ok(StructType::NonNewtypeTuple)
            } else {
                Ok(StructType::NewtypeTuple)
            }
        }

        // Create a temporary working copy
        let backup_cursor = self.cursor;

        let result = check_struct_type_inner(self, newtype, tuple);

        if result.is_ok() {
            // Revert the parser to before the struct type check
            self.set_cursor(backup_cursor);
        }

        result
    }

    /// Only returns true if the char after `ident` cannot belong
    /// to an identifier.
    pub fn consume_ident(&mut self, ident: &str) -> bool {
        if self.check_ident(ident) {
            self.advance_bytes(ident.len());

            true
        } else {
            false
        }
    }

    pub fn consume_struct_name(&mut self, ident: &'static str) -> Result<bool> {
        if self.check_ident("") {
            if self.exts.contains(Extensions::EXPLICIT_STRUCT_NAMES) {
                return Err(Error::ExpectedStructName(ident.to_string()));
            }

            return Ok(false);
        }

        let found_ident = match self.identifier() {
            Ok(maybe_ident) => maybe_ident,
            Err(Error::SuggestRawIdentifier(found_ident)) if found_ident == ident => {
                return Err(Error::SuggestRawIdentifier(found_ident))
            }
            Err(_) => return Err(Error::ExpectedNamedStructLike(ident)),
        };

        if ident.is_empty() {
            return Err(Error::ExpectedNamedStructLike(ident));
        }

        if found_ident != ident {
            return Err(Error::ExpectedDifferentStructName {
                expected: ident,
                found: String::from(found_ident),
            });
        }

        Ok(true)
    }

    /// Returns the extensions bit mask.
    fn extensions(&mut self) -> Result<Extensions> {
        if !self.check_char('#') {
            return Ok(Extensions::empty());
        }

        if !self.consume_all(&["#", "!", "[", "enable", "("])? {
            return Err(Error::ExpectedAttribute);
        }

        self.skip_ws()?;
        let mut extensions = Extensions::empty();

        loop {
            let ident = self.identifier()?;
            let extension = Extensions::from_ident(ident)
                .ok_or_else(|| Error::NoSuchExtension(ident.into()))?;

            extensions |= extension;

            let comma = self.comma()?;

            // If we have no comma but another item, return an error
            if !comma && self.check_ident_other_char(0) {
                return Err(Error::ExpectedComma);
            }

            // If there's no comma, assume the list ended.
            // If there is, it might be a trailing one, thus we only
            // continue the loop if we get an ident char.
            if !comma || !self.check_ident_other_char(0) {
                break;
            }
        }

        self.skip_ws()?;

        if self.consume_all(&[")", "]"])? {
            Ok(extensions)
        } else {
            Err(Error::ExpectedAttributeEnd)
        }
    }

    pub fn float<T: Float>(&mut self) -> Result<T> {
        const F32_SUFFIX: &str = "f32";
        const F64_SUFFIX: &str = "f64";

        for (literal, value_f32, value_f64) in &[
            ("inf", f32::INFINITY, f64::INFINITY),
            ("+inf", f32::INFINITY, f64::INFINITY),
            ("-inf", f32::NEG_INFINITY, f64::NEG_INFINITY),
            ("NaN", f32::NAN, f64::NAN),
            ("+NaN", f32::NAN, f64::NAN),
            ("-NaN", -f32::NAN, -f64::NAN),
        ] {
            if self.consume_ident(literal) {
                return T::parse(literal);
            }

            if let Some(suffix) = self.src().strip_prefix(literal) {
                if let Some(post_suffix) = suffix.strip_prefix(F32_SUFFIX) {
                    if !post_suffix.chars().next().map_or(false, is_xid_continue) {
                        let float_ron = &self.src()[..literal.len() + F32_SUFFIX.len()];
                        self.advance_bytes(literal.len() + F32_SUFFIX.len());
                        return T::try_from_parsed_float(ParsedFloat::F32(*value_f32), float_ron);
                    }
                }

                if let Some(post_suffix) = suffix.strip_prefix(F64_SUFFIX) {
                    if !post_suffix.chars().next().map_or(false, is_xid_continue) {
                        let float_ron = &self.src()[..literal.len() + F64_SUFFIX.len()];
                        self.advance_bytes(literal.len() + F64_SUFFIX.len());
                        return T::try_from_parsed_float(ParsedFloat::F64(*value_f64), float_ron);
                    }
                }
            }
        }

        let num_bytes = self.next_chars_while_len(is_float_char);

        if num_bytes == 0 {
            return Err(Error::ExpectedFloat);
        }

        if self.check_char('_') {
            return Err(Error::UnderscoreAtBeginning);
        }

        let mut f = String::with_capacity(num_bytes);
        let mut allow_underscore = false;

        for (i, c) in self.src()[..num_bytes].char_indices() {
            match c {
                '_' if allow_underscore => continue,
                '_' => {
                    self.advance_bytes(i);
                    return Err(Error::FloatUnderscore);
                }
                '0'..='9' | 'e' | 'E' => allow_underscore = true,
                '.' => allow_underscore = false,
                _ => (),
            }

            // we know that the byte is an ASCII character here
            f.push(c);
        }

        if self.src()[num_bytes..].starts_with('f') {
            let backup_cursor = self.cursor;
            self.advance_bytes(num_bytes);

            #[allow(clippy::never_loop)]
            loop {
                let res = if self.consume_ident(F32_SUFFIX) {
                    f32::from_str(&f).map(ParsedFloat::F32)
                } else if self.consume_ident(F64_SUFFIX) {
                    f64::from_str(&f).map(ParsedFloat::F64)
                } else {
                    break;
                };

                let parsed = if let Ok(parsed) = res {
                    parsed
                } else {
                    self.set_cursor(backup_cursor);
                    return Err(Error::ExpectedFloat);
                };

                let float_ron = &self.src[backup_cursor.cursor..self.cursor.cursor];

                return T::try_from_parsed_float(parsed, float_ron);
            }

            self.set_cursor(backup_cursor);
        }

        let value = T::parse(&f)?;

        self.advance_bytes(num_bytes);

        Ok(value)
    }

    pub fn skip_identifier(&mut self) -> Option<&'a str> {
        #[allow(clippy::nonminimal_bool)]
        if self.check_str("b\"") // byte string
            || self.check_str("b'") // byte literal
            || self.check_str("br#") // raw byte string
            || self.check_str("br\"") // raw byte string
            || self.check_str("r\"") // raw string
            || self.check_str("r#\"") // raw string
            || self.check_str("r##") // raw string
            || false
        {
            return None;
        }

        if self.check_str("r#") {
            // maybe a raw identifier
            let len = self.next_chars_while_from_len(2, is_ident_raw_char);
            if len > 0 {
                let ident = &self.src()[2..2 + len];
                self.advance_bytes(2 + len);
                return Some(ident);
            }
            return None;
        }

        if let Some(c) = self.peek_char() {
            // maybe a normal identifier
            if is_ident_first_char(c) {
                let len =
                    c.len_utf8() + self.next_chars_while_from_len(c.len_utf8(), is_xid_continue);
                let ident = &self.src()[..len];
                self.advance_bytes(len);
                return Some(ident);
            }
        }

        None
    }

    pub fn identifier(&mut self) -> Result<&'a str> {
        let first = self.peek_char_or_eof()?;
        if !is_ident_first_char(first) {
            if is_ident_raw_char(first) {
                let ident_bytes = self.next_chars_while_len(is_ident_raw_char);
                return Err(Error::SuggestRawIdentifier(
                    self.src()[..ident_bytes].into(),
                ));
            }

            return Err(Error::ExpectedIdentifier);
        }

        // If the next 2-3 bytes signify the start of a (raw) (byte) string
        //  literal, return an error.
        #[allow(clippy::nonminimal_bool)]
        if self.check_str("b\"") // byte string
            || self.check_str("b'") // byte literal
            || self.check_str("br#") // raw byte string
            || self.check_str("br\"") // raw byte string
            || self.check_str("r\"") // raw string
            || self.check_str("r#\"") // raw string
            || self.check_str("r##") // raw string
            || false
        {
            return Err(Error::ExpectedIdentifier);
        }

        let length = if self.check_str("r#") {
            let cursor_backup = self.cursor;

            self.advance_bytes(2);

            // Note: it's important to check this before advancing forward, so that
            // the value-type deserializer can fall back to parsing it differently.
            if !matches!(self.peek_char(), Some(c) if is_ident_raw_char(c)) {
                self.set_cursor(cursor_backup);
                return Err(Error::ExpectedIdentifier);
            }

            self.next_chars_while_len(is_ident_raw_char)
        } else if first == 'r' {
            let std_ident_length = self.next_chars_while_len(is_xid_continue);
            let raw_ident_length = self.next_chars_while_len(is_ident_raw_char);

            if raw_ident_length > std_ident_length {
                return Err(Error::SuggestRawIdentifier(
                    self.src()[..raw_ident_length].into(),
                ));
            }

            std_ident_length
        } else {
            let std_ident_length = first.len_utf8()
                + self.next_chars_while_from_len(first.len_utf8(), is_xid_continue);
            let raw_ident_length = self.next_chars_while_len(is_ident_raw_char);

            if raw_ident_length > std_ident_length {
                return Err(Error::SuggestRawIdentifier(
                    self.src()[..raw_ident_length].into(),
                ));
            }

            std_ident_length
        };

        let ident = &self.src()[..length];
        self.advance_bytes(length);

        Ok(ident)
    }

    pub fn next_bytes_is_float(&mut self) -> bool {
        if let Some(c) = self.peek_char() {
            let skip = match c {
                '+' | '-' => 1,
                _ => 0,
            };
            let valid_float_len = self.next_chars_while_from_len(skip, is_float_char);
            let valid_int_len = self.next_chars_while_from_len(skip, is_int_char);
            valid_float_len > valid_int_len
        } else {
            false
        }
    }

    pub fn skip_ws(&mut self) -> Result<()> {
        if (self.cursor.last_ws_len != WS_CURSOR_UNCLOSED_LINE)
            && ((self.cursor.pre_ws_cursor + self.cursor.last_ws_len) < self.cursor.cursor)
        {
            // the last whitespace is disjoint from this one, we need to track a new one
            self.cursor.pre_ws_cursor = self.cursor.cursor;
        }

        if self.src().is_empty() {
            return Ok(());
        }

        loop {
            self.advance_bytes(self.next_chars_while_len(is_whitespace_char));

            match self.skip_comment()? {
                None => break,
                Some(Comment::UnclosedLine) => {
                    self.cursor.last_ws_len = WS_CURSOR_UNCLOSED_LINE;
                    return Ok(());
                }
                Some(Comment::ClosedLine | Comment::Block) => continue,
            }
        }

        self.cursor.last_ws_len = self.cursor.cursor - self.cursor.pre_ws_cursor;

        Ok(())
    }

    pub fn has_unclosed_line_comment(&self) -> bool {
        self.src().is_empty() && self.cursor.last_ws_len == WS_CURSOR_UNCLOSED_LINE
    }

    pub fn byte_string(&mut self) -> Result<ParsedByteStr<'a>> {
        fn expected_byte_string_found_base64(
            base64_str: &ParsedStr,
            byte_str: &ParsedByteStr,
        ) -> Error {
            let byte_str = match &byte_str {
                ParsedByteStr::Allocated(b) => b.as_slice(),
                ParsedByteStr::Slice(b) => b,
            }
            .iter()
            .flat_map(|c| std::ascii::escape_default(*c))
            .map(char::from)
            .collect::<String>();
            let base64_str = match &base64_str {
                ParsedStr::Allocated(s) => s.as_str(),
                ParsedStr::Slice(s) => s,
            };

            Error::InvalidValueForType {
                expected: format!("the Rusty byte string b\"{}\"", byte_str),
                found: format!("the ambiguous base64 string {:?}", base64_str),
            }
        }

        if self.consume_char('"') {
            let base64_str = self.escaped_string()?;
            let base64_result = ParsedByteStr::try_from_base64(&base64_str);

            if cfg!(not(test)) {
                // FIXME @juntyr: remove in v0.10
                #[allow(deprecated)]
                base64_result.map_err(Error::Base64Error)
            } else {
                match base64_result {
                    // FIXME @juntyr: enable in v0.10
                    Ok(byte_str) => Err(expected_byte_string_found_base64(&base64_str, &byte_str)),
                    Err(_) => Err(Error::ExpectedByteString),
                }
            }
        } else if self.consume_char('r') {
            let base64_str = self.raw_string()?;
            let base64_result = ParsedByteStr::try_from_base64(&base64_str);

            if cfg!(not(test)) {
                // FIXME @juntyr: remove in v0.10
                #[allow(deprecated)]
                base64_result.map_err(Error::Base64Error)
            } else {
                match base64_result {
                    // FIXME @juntyr: enable in v0.10
                    Ok(byte_str) => Err(expected_byte_string_found_base64(&base64_str, &byte_str)),
                    Err(_) => Err(Error::ExpectedByteString),
                }
            }
        } else {
            self.byte_string_no_base64()
        }
    }

    pub fn byte_string_no_base64(&mut self) -> Result<ParsedByteStr<'a>> {
        if self.consume_str("b\"") {
            self.escaped_byte_string()
        } else if self.consume_str("br") {
            self.raw_byte_string()
        } else {
            Err(Error::ExpectedByteString)
        }
    }

    fn escaped_byte_string(&mut self) -> Result<ParsedByteStr<'a>> {
        match self.escaped_byte_buf(EscapeEncoding::Binary) {
            Ok((bytes, advance)) => {
                self.advance_bytes(advance);
                Ok(bytes)
            }
            Err(err) => Err(err),
        }
    }

    fn raw_byte_string(&mut self) -> Result<ParsedByteStr<'a>> {
        match self.raw_byte_buf() {
            Ok((bytes, advance)) => {
                self.advance_bytes(advance);
                Ok(bytes)
            }
            Err(Error::ExpectedString) => Err(Error::ExpectedByteString),
            Err(err) => Err(err),
        }
    }

    pub fn string(&mut self) -> Result<ParsedStr<'a>> {
        if self.consume_char('"') {
            self.escaped_string()
        } else if self.consume_char('r') {
            self.raw_string()
        } else {
            Err(Error::ExpectedString)
        }
    }

    fn escaped_string(&mut self) -> Result<ParsedStr<'a>> {
        match self.escaped_byte_buf(EscapeEncoding::Utf8) {
            Ok((bytes, advance)) => {
                let string = ParsedStr::try_from_bytes(bytes).map_err(Error::from)?;
                self.advance_bytes(advance);
                Ok(string)
            }
            Err(err) => Err(err),
        }
    }

    fn raw_string(&mut self) -> Result<ParsedStr<'a>> {
        match self.raw_byte_buf() {
            Ok((bytes, advance)) => {
                let string = ParsedStr::try_from_bytes(bytes).map_err(Error::from)?;
                self.advance_bytes(advance);
                Ok(string)
            }
            Err(err) => Err(err),
        }
    }

    fn escaped_byte_buf(&mut self, encoding: EscapeEncoding) -> Result<(ParsedByteStr<'a>, usize)> {
        // Checking for '"' and '\\' separately is faster than searching for both at the same time
        let str_end = self.src().find('"').ok_or(Error::ExpectedStringEnd)?;
        let escape = self.src()[..str_end].find('\\');

        if let Some(escape) = escape {
            // Now check if escaping is used inside the string
            let mut i = escape;
            let mut s = self.src().as_bytes()[..i].to_vec();

            loop {
                self.advance_bytes(i + 1);

                match self.parse_escape(encoding, false)? {
                    EscapeCharacter::Ascii(c) => s.push(c),
                    EscapeCharacter::Utf8(c) => match c.len_utf8() {
                        1 => s.push(c as u8),
                        len => {
                            let start = s.len();
                            s.extend(std::iter::repeat(0).take(len));
                            c.encode_utf8(&mut s[start..]);
                        }
                    },
                }

                // Checking for '"' and '\\' separately is faster than searching for both at the same time
                let new_str_end = self.src().find('"').ok_or(Error::ExpectedStringEnd)?;
                let new_escape = self.src()[..new_str_end].find('\\');

                if let Some(new_escape) = new_escape {
                    s.extend_from_slice(&self.src().as_bytes()[..new_escape]);
                    i = new_escape;
                } else {
                    s.extend_from_slice(&self.src().as_bytes()[..new_str_end]);
                    // Advance to the end of the string + 1 for the `"`.
                    break Ok((ParsedByteStr::Allocated(s), new_str_end + 1));
                }
            }
        } else {
            let s = &self.src().as_bytes()[..str_end];

            // Advance by the number of bytes of the string + 1 for the `"`.
            Ok((ParsedByteStr::Slice(s), str_end + 1))
        }
    }

    fn raw_byte_buf(&mut self) -> Result<(ParsedByteStr<'a>, usize)> {
        let num_hashes = self.next_chars_while_len(|c| c == '#');
        let hashes = &self.src()[..num_hashes];
        self.advance_bytes(num_hashes);

        self.expect_char('"', Error::ExpectedString)?;

        let ending = ["\"", hashes].concat();
        let i = self.src().find(&ending).ok_or(Error::ExpectedStringEnd)?;

        let s = &self.src().as_bytes()[..i];

        // Advance by the number of bytes of the byte string
        // + `num_hashes` + 1 for the `"`.
        Ok((ParsedByteStr::Slice(s), i + num_hashes + 1))
    }

    fn decode_ascii_escape(&mut self) -> Result<u8> {
        let mut n = 0;
        for _ in 0..2 {
            n <<= 4;
            let byte = self.next_char()?;
            let decoded = Self::decode_hex(byte)?;
            n |= decoded;
        }

        Ok(n)
    }

    #[inline]
    fn decode_hex(c: char) -> Result<u8> {
        if !c.is_ascii() {
            return Err(Error::InvalidEscape("Non-hex digit found"));
        }

        // c is an ASCII character that can be losslessly cast to u8
        match c as u8 {
            c @ b'0'..=b'9' => Ok(c - b'0'),
            c @ b'a'..=b'f' => Ok(10 + c - b'a'),
            c @ b'A'..=b'F' => Ok(10 + c - b'A'),
            _ => Err(Error::InvalidEscape("Non-hex digit found")),
        }
    }

    fn parse_escape(&mut self, encoding: EscapeEncoding, is_char: bool) -> Result<EscapeCharacter> {
        let c = match self.next_char()? {
            '\'' => EscapeCharacter::Ascii(b'\''),
            '"' => EscapeCharacter::Ascii(b'"'),
            '\\' => EscapeCharacter::Ascii(b'\\'),
            'n' => EscapeCharacter::Ascii(b'\n'),
            'r' => EscapeCharacter::Ascii(b'\r'),
            't' => EscapeCharacter::Ascii(b'\t'),
            '0' => EscapeCharacter::Ascii(b'\0'),
            'x' => {
                // Fast exit for ascii escape in byte string
                let b: u8 = self.decode_ascii_escape()?;
                if let EscapeEncoding::Binary = encoding {
                    return Ok(EscapeCharacter::Ascii(b));
                }

                // Fast exit for ascii character in UTF-8 string
                let mut bytes = [b, 0, 0, 0];
                if let Ok(Some(c)) = from_utf8(&bytes[..=0]).map(|s| s.chars().next()) {
                    return Ok(EscapeCharacter::Utf8(c));
                }

                if is_char {
                    // Character literals are not allowed to use multiple byte
                    //  escapes to build a unicode character
                    return Err(Error::InvalidEscape(
                        "Not a valid byte-escaped Unicode character",
                    ));
                }

                // UTF-8 character needs up to four bytes and we have already
                //  consumed one, so at most three to go
                for i in 1..4 {
                    if !self.consume_str(r"\x") {
                        return Err(Error::InvalidEscape(
                            "Not a valid byte-escaped Unicode character",
                        ));
                    }

                    bytes[i] = self.decode_ascii_escape()?;

                    // Check if we now have a valid UTF-8 character
                    if let Ok(Some(c)) = from_utf8(&bytes[..=i]).map(|s| s.chars().next()) {
                        return Ok(EscapeCharacter::Utf8(c));
                    }
                }

                return Err(Error::InvalidEscape(
                    "Not a valid byte-escaped Unicode character",
                ));
            }
            'u' => {
                self.expect_char('{', Error::InvalidEscape("Missing { in Unicode escape"))?;

                let mut bytes: u32 = 0;
                let mut num_digits = 0;

                while num_digits < 6 {
                    let byte = self.peek_char_or_eof()?;

                    if byte == '}' {
                        break;
                    }

                    self.skip_next_char();
                    num_digits += 1;

                    let byte = Self::decode_hex(byte)?;
                    bytes <<= 4;
                    bytes |= u32::from(byte);
                }

                if num_digits == 0 {
                    return Err(Error::InvalidEscape(
                        "Expected 1-6 digits, got 0 digits in Unicode escape",
                    ));
                }

                self.expect_char(
                    '}',
                    Error::InvalidEscape("No } at the end of Unicode escape"),
                )?;
                let c = char_from_u32(bytes).ok_or(Error::InvalidEscape(
                    "Not a valid Unicode-escaped character",
                ))?;

                EscapeCharacter::Utf8(c)
            }
            _ => return Err(Error::InvalidEscape("Unknown escape character")),
        };

        Ok(c)
    }

    fn skip_comment(&mut self) -> Result<Option<Comment>> {
        if self.consume_char('/') {
            match self.next_char()? {
                '/' => {
                    let bytes = self.next_chars_while_len(|c| c != '\n');

                    self.advance_bytes(bytes);

                    if self.src().is_empty() {
                        Ok(Some(Comment::UnclosedLine))
                    } else {
                        Ok(Some(Comment::ClosedLine))
                    }
                }
                '*' => {
                    let mut level = 1;

                    while level > 0 {
                        let bytes = self.next_chars_while_len(|c| !matches!(c, '/' | '*'));

                        if self.src().is_empty() {
                            return Err(Error::UnclosedBlockComment);
                        }

                        self.advance_bytes(bytes);

                        // check whether / or * and take action
                        if self.consume_str("/*") {
                            level += 1;
                        } else if self.consume_str("*/") {
                            level -= 1;
                        } else {
                            self.next_char().map_err(|_| Error::UnclosedBlockComment)?;
                        }
                    }

                    Ok(Some(Comment::Block))
                }
                c => Err(Error::UnexpectedChar(c)),
            }
        } else {
            Ok(None)
        }
    }
}

enum Comment {
    ClosedLine,
    UnclosedLine,
    Block,
}

pub trait Num {
    fn from_u8(x: u8) -> Self;

    /// Returns `true` on overflow
    fn checked_mul_ext(&mut self, x: u8) -> bool;

    /// Returns `true` on overflow
    fn checked_add_ext(&mut self, x: u8) -> bool;

    /// Returns `true` on overflow
    fn checked_sub_ext(&mut self, x: u8) -> bool;
}

macro_rules! impl_num {
    ($ty:ty) => {
        impl Num for $ty {
            fn from_u8(x: u8) -> Self {
                x as $ty
            }

            fn checked_mul_ext(&mut self, x: u8) -> bool {
                match self.checked_mul(Self::from_u8(x)) {
                    Some(n) => {
                        *self = n;
                        false
                    }
                    None => true,
                }
            }

            fn checked_add_ext(&mut self, x: u8) -> bool {
                match self.checked_add(Self::from_u8(x)) {
                    Some(n) => {
                        *self = n;
                        false
                    }
                    None => true,
                }
            }

            fn checked_sub_ext(&mut self, x: u8) -> bool {
                match self.checked_sub(Self::from_u8(x)) {
                    Some(n) => {
                        *self = n;
                        false
                    }
                    None => true,
                }
            }
        }
    };
    ($($tys:ty)*) => {
        $( impl_num!($tys); )*
    };
}

impl_num! { i8 i16 i32 i64 u8 u16 u32 u64 }

#[cfg(feature = "integer128")]
impl_num! { i128 u128 }

pub trait Integer: Sized {
    fn parse(parser: &mut Parser, sign: i8) -> Result<Self>;

    fn try_from_parsed_integer(parsed: ParsedInteger, ron: &str) -> Result<Self>;
}

macro_rules! impl_integer {
    ($wrap:ident($ty:ty)) => {
        impl Integer for $ty {
            fn parse(parser: &mut Parser, sign: i8) -> Result<Self> {
                parser.parse_integer(sign)
            }

            fn try_from_parsed_integer(parsed: ParsedInteger, ron: &str) -> Result<Self> {
                match parsed {
                    ParsedInteger::$wrap(v) => Ok(v),
                    _ => Err(Error::InvalidValueForType {
                        expected: format!(
                            "a{} {}-bit {}signed integer",
                            if <$ty>::BITS == 8 { "n" } else { "n" },
                            <$ty>::BITS,
                            if <$ty>::MIN == 0 { "un" } else { "" },
                        ),
                        found: String::from(ron),
                    }),
                }
            }
        }
    };
    ($($wraps:ident($tys:ty))*) => {
        $( impl_integer!($wraps($tys)); )*
    };
}

impl_integer! {
    I8(i8) I16(i16) I32(i32) I64(i64)
    U8(u8) U16(u16) U32(u32) U64(u64)
}

#[cfg(feature = "integer128")]
impl_integer! { I128(i128) U128(u128) }

pub enum ParsedInteger {
    I8(i8),
    I16(i16),
    I32(i32),
    I64(i64),
    #[cfg(feature = "integer128")]
    I128(i128),
    U8(u8),
    U16(u16),
    U32(u32),
    U64(u64),
    #[cfg(feature = "integer128")]
    U128(u128),
}

impl Integer for ParsedInteger {
    fn parse(parser: &mut Parser, sign: i8) -> Result<Self> {
        if sign < 0 {
            let signed = parser.parse_integer::<LargeSInt>(-1)?;

            return if let Ok(x) = i8::try_from(signed) {
                Ok(ParsedInteger::I8(x))
            } else if let Ok(x) = i16::try_from(signed) {
                Ok(ParsedInteger::I16(x))
            } else if let Ok(x) = i32::try_from(signed) {
                Ok(ParsedInteger::I32(x))
            } else {
                #[cfg(not(feature = "integer128"))]
                {
                    Ok(ParsedInteger::I64(signed))
                }
                #[cfg(feature = "integer128")]
                if let Ok(x) = i64::try_from(signed) {
                    Ok(ParsedInteger::I64(x))
                } else {
                    Ok(ParsedInteger::I128(signed))
                }
            };
        }

        let unsigned = parser.parse_integer::<LargeUInt>(1)?;

        if let Ok(x) = u8::try_from(unsigned) {
            Ok(ParsedInteger::U8(x))
        } else if let Ok(x) = u16::try_from(unsigned) {
            Ok(ParsedInteger::U16(x))
        } else if let Ok(x) = u32::try_from(unsigned) {
            Ok(ParsedInteger::U32(x))
        } else {
            #[cfg(not(feature = "integer128"))]
            {
                Ok(ParsedInteger::U64(unsigned))
            }
            #[cfg(feature = "integer128")]
            if let Ok(x) = u64::try_from(unsigned) {
                Ok(ParsedInteger::U64(x))
            } else {
                Ok(ParsedInteger::U128(unsigned))
            }
        }
    }

    fn try_from_parsed_integer(parsed: ParsedInteger, _ron: &str) -> Result<Self> {
        Ok(parsed)
    }
}

pub trait Float: Sized {
    fn parse(float: &str) -> Result<Self>;

    fn try_from_parsed_float(parsed: ParsedFloat, ron: &str) -> Result<Self>;
}

macro_rules! impl_float {
    ($wrap:ident($ty:ty: $bits:expr)) => {
        impl Float for $ty {
            fn parse(float: &str) -> Result<Self> {
                <$ty>::from_str(float).map_err(|_| Error::ExpectedFloat)
            }

            fn try_from_parsed_float(parsed: ParsedFloat, ron: &str) -> Result<Self> {
                match parsed {
                    ParsedFloat::$wrap(v) => Ok(v),
                    _ => Err(Error::InvalidValueForType {
                        expected: format!(
                            "a {}-bit floating point number", $bits,
                        ),
                        found: String::from(ron),
                    }),
                }
            }
        }
    };
    ($($wraps:ident($tys:ty: $bits:expr))*) => {
        $( impl_float!($wraps($tys: $bits)); )*
    };
}

impl_float! { F32(f32: 32) F64(f64: 64) }

pub enum ParsedFloat {
    F32(f32),
    F64(f64),
}

impl Float for ParsedFloat {
    fn parse(float: &str) -> Result<Self> {
        let value = f64::from_str(float).map_err(|_| Error::ExpectedFloat)?;

        #[allow(clippy::cast_possible_truncation)]
        if value.total_cmp(&f64::from(value as f32)).is_eq() {
            Ok(ParsedFloat::F32(value as f32))
        } else {
            Ok(ParsedFloat::F64(value))
        }
    }

    fn try_from_parsed_float(parsed: ParsedFloat, _ron: &str) -> Result<Self> {
        Ok(parsed)
    }
}

pub enum StructType {
    AnyTuple,
    EmptyTuple,
    NewtypeTuple,
    NonNewtypeTuple,
    Named,
    Unit,
}

#[derive(Copy, Clone)] // GRCOV_EXCL_LINE
pub enum NewtypeMode {
    NoParensMeanUnit,
    InsideNewtype,
}

#[derive(Copy, Clone)] // GRCOV_EXCL_LINE
pub enum TupleMode {
    ImpreciseTupleOrNewtype,
    DifferentiateNewtype,
}

pub enum ParsedStr<'a> {
    Allocated(String),
    Slice(&'a str),
}

pub enum ParsedByteStr<'a> {
    Allocated(Vec<u8>),
    Slice(&'a [u8]),
}

impl<'a> ParsedStr<'a> {
    pub fn try_from_bytes(bytes: ParsedByteStr<'a>) -> Result<Self, Utf8Error> {
        match bytes {
            ParsedByteStr::Allocated(byte_buf) => Ok(ParsedStr::Allocated(
                String::from_utf8(byte_buf).map_err(|e| e.utf8_error())?,
            )),
            ParsedByteStr::Slice(bytes) => Ok(ParsedStr::Slice(from_utf8(bytes)?)),
        }
    }
}

impl<'a> ParsedByteStr<'a> {
    pub fn try_from_base64(str: &ParsedStr<'a>) -> Result<Self, base64::DecodeError> {
        let base64_str = match str {
            ParsedStr::Allocated(string) => string.as_str(),
            ParsedStr::Slice(str) => str,
        };

        base64::engine::Engine::decode(&base64::engine::general_purpose::STANDARD, base64_str)
            .map(ParsedByteStr::Allocated)
    }
}

#[derive(Copy, Clone)] // GRCOV_EXCL_LINE
enum EscapeEncoding {
    Binary,
    Utf8,
}

enum EscapeCharacter {
    Ascii(u8),
    Utf8(char),
}

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

    #[test]
    fn decode_x10() {
        let mut bytes = Parser::new("10").unwrap();
        assert_eq!(bytes.decode_ascii_escape(), Ok(b'\x10'));
    }

    #[test]
    fn track_prior_ws() {
        const SOURCE: &str = "   /*hey*/ 42       /*bye*/ 24  ";
        let mut bytes = Parser::new(SOURCE).unwrap();

        assert_eq!(bytes.src(), "42       /*bye*/ 24  ");
        assert_eq!(bytes.pre_ws_src(), SOURCE);

        bytes.skip_ws().unwrap();

        assert_eq!(bytes.src(), "42       /*bye*/ 24  ");
        assert_eq!(bytes.pre_ws_src(), SOURCE);

        assert_eq!(bytes.integer::<u8>().unwrap(), 42);

        assert_eq!(bytes.src(), "       /*bye*/ 24  ");
        assert_eq!(bytes.pre_ws_src(), SOURCE);

        bytes.skip_ws().unwrap();
        bytes.skip_ws().unwrap();

        assert_eq!(bytes.src(), "24  ");
        assert_eq!(bytes.pre_ws_src(), "       /*bye*/ 24  ");

        let mut bytes = Parser::new("42").unwrap();
        bytes.skip_ws().unwrap();
        bytes.skip_ws().unwrap();
        assert_eq!(bytes.src(), "42");
        assert_eq!(bytes.pre_ws_src(), "42");
        assert_eq!(bytes.integer::<u8>().unwrap(), 42);
        bytes.skip_ws().unwrap();
        bytes.skip_ws().unwrap();
        assert_eq!(bytes.src(), "");
        assert_eq!(bytes.pre_ws_src(), "");

        let mut bytes = Parser::new("  42  ").unwrap();
        bytes.skip_ws().unwrap();
        bytes.skip_ws().unwrap();
        assert_eq!(bytes.src(), "42  ");
        assert_eq!(bytes.pre_ws_src(), "  42  ");
        assert_eq!(bytes.integer::<u8>().unwrap(), 42);
        bytes.skip_ws().unwrap();
        bytes.skip_ws().unwrap();
        assert_eq!(bytes.src(), "");
        assert_eq!(bytes.pre_ws_src(), "  ");

        let mut bytes = Parser::new("  42  //").unwrap();
        bytes.skip_ws().unwrap();
        bytes.skip_ws().unwrap();
        assert_eq!(bytes.src(), "42  //");
        assert_eq!(bytes.pre_ws_src(), "  42  //");
        assert_eq!(bytes.integer::<u8>().unwrap(), 42);
        bytes.skip_ws().unwrap();
        bytes.skip_ws().unwrap();
        assert_eq!(bytes.src(), "");
        assert_eq!(bytes.pre_ws_src(), "  //");
    }

    #[test]
    fn parser_cursor_eq_cmp() {
        assert!(
            ParserCursor {
                cursor: 42,
                pre_ws_cursor: 42,
                last_ws_len: 42
            } == ParserCursor {
                cursor: 42,
                pre_ws_cursor: 24,
                last_ws_len: 24
            }
        );
        assert!(
            ParserCursor {
                cursor: 42,
                pre_ws_cursor: 42,
                last_ws_len: 42
            } != ParserCursor {
                cursor: 24,
                pre_ws_cursor: 42,
                last_ws_len: 42
            }
        );

        assert!(
            ParserCursor {
                cursor: 42,
                pre_ws_cursor: 42,
                last_ws_len: 42
            } < ParserCursor {
                cursor: 43,
                pre_ws_cursor: 24,
                last_ws_len: 24
            }
        );
        assert!(
            ParserCursor {
                cursor: 42,
                pre_ws_cursor: 42,
                last_ws_len: 42
            } > ParserCursor {
                cursor: 41,
                pre_ws_cursor: 24,
                last_ws_len: 24
            }
        );
    }

    #[test]
    fn empty_src_is_not_a_float() {
        assert!(!Parser::new("").unwrap().next_bytes_is_float());
    }

    #[test]
    fn v0_10_base64_deprecation_error() {
        let err = crate::from_str::<bytes::Bytes>("\"SGVsbG8gcm9uIQ==\"").unwrap_err();

        assert_eq!(
            err,
            SpannedError {
                code: Error::InvalidValueForType {
                    expected: String::from("the Rusty byte string b\"Hello ron!\""),
                    found: String::from("the ambiguous base64 string \"SGVsbG8gcm9uIQ==\"")
                },
                position: Position { line: 1, col: 19 },
            }
        );

        let err = crate::from_str::<bytes::Bytes>("r\"SGVsbG8gcm9uIQ==\"").unwrap_err();

        assert_eq!(format!("{}", err.code), "Expected the Rusty byte string b\"Hello ron!\" but found the ambiguous base64 string \"SGVsbG8gcm9uIQ==\" instead");

        assert_eq!(
            crate::from_str::<bytes::Bytes>("\"invalid=\"").unwrap_err(),
            SpannedError {
                code: Error::ExpectedByteString,
                position: Position { line: 1, col: 11 },
            }
        );

        assert_eq!(
            crate::from_str::<bytes::Bytes>("r\"invalid=\"").unwrap_err(),
            SpannedError {
                code: Error::ExpectedByteString,
                position: Position { line: 1, col: 12 },
            }
        );
    }
}
