dangerous 0.10.0

Safely and explicitly parse untrusted / dangerous data
Documentation
use crate::input::{Input, Pattern, Prefix, PrivateExt};

use crate::error::{
    with_context, Context, CoreContext, CoreOperation, ExpectedLength, ExpectedValid,
    ExpectedValue, External, Value, WithContext,
};

use super::{Peek, Reader};

impl<'i, I, E> Reader<'i, I, E>
where
    I: Input<'i>,
{
    /// Returns `true` if the `Reader` has no more input to consume.
    #[must_use]
    #[inline(always)]
    pub fn at_end(&self) -> bool {
        self.input.is_empty()
    }

    /// Returns the number of input bytes left within the reader.
    ///
    /// This number is subject to change in future passes for streaming input.
    pub fn remaining_bytes(&self) -> usize {
        self.input.byte_len()
    }

    /// Read all of the remaining input.
    #[inline(always)]
    pub fn take_remaining(&mut self) -> I {
        self.advance(|input| (input.clone(), input.end()))
    }

    /// Mutably use the `Reader` with a given context.
    ///
    /// # Errors
    ///
    /// Returns any error returned by the provided function with the specified
    /// context attached.
    #[inline(always)]
    pub fn context<F, T>(&mut self, context: impl Context, f: F) -> Result<T, E>
    where
        E: WithContext<'i>,
        F: FnOnce(&mut Self) -> Result<T, E>,
    {
        with_context(context, self.input.clone(), || f(self))
    }

    /// Immutably use the `Reader` with a given context.
    ///
    /// # Errors
    ///
    /// Returns any error returned by the provided function with the specified
    /// context attached.
    #[inline(always)]
    pub fn peek_context<F, T>(&self, context: impl Context, f: F) -> Result<T, E>
    where
        E: WithContext<'i>,
        F: FnOnce(&Self) -> Result<T, E>,
    {
        with_context(context, self.input.clone(), || f(self))
    }

    /// Read a length of input that was successfully consumed from a sub-parse.
    pub fn take_consumed<F, T>(&mut self, consumer: F) -> (T, I)
    where
        F: FnOnce(&mut Self) -> T,
    {
        self.advance(|input| {
            let (value, head, tail) = input.split_consumed(consumer);
            ((value, head), tail)
        })
    }

    /// Try read a length of input that was successfully consumed from a
    /// sub-parse.
    ///
    /// # Example
    ///
    /// ```
    /// use dangerous::{Input, Invalid};
    ///
    /// let result: Result<_, Invalid> = dangerous::input(b"abc").read_all(|r| {
    ///     let ((), consumed) = r.try_take_consumed(|r| {
    ///         r.skip(1)?;
    ///         r.consume(b"bc")?;
    ///         Ok(())
    ///     })?;
    ///     Ok(consumed)
    /// });
    ///
    /// assert_eq!(result.unwrap(), b"abc"[..]);
    /// ```
    ///
    /// # Errors
    ///
    /// Returns any error the provided function does.
    pub fn try_take_consumed<F, T>(&mut self, consumer: F) -> Result<(T, I), E>
    where
        E: WithContext<'i>,
        F: FnOnce(&mut Self) -> Result<T, E>,
    {
        self.try_advance(|input| {
            input
                .try_split_consumed(consumer, CoreOperation::TakeConsumed)
                .map(|(value, head, tail)| ((value, head), tail))
        })
    }

    /// Read and verify a value without returning it.
    ///
    /// # Errors
    ///
    /// Returns an error if the verifier function returned `false`.
    pub fn verify<F>(&mut self, expected: &'static str, verifier: F) -> Result<(), E>
    where
        F: FnOnce(&mut Self) -> bool,
        E: From<ExpectedValid<'i>>,
    {
        self.try_advance(|input| {
            input.split_expect(
                |r: &mut Self| {
                    if verifier(r) {
                        Some(())
                    } else {
                        None
                    }
                },
                expected,
                CoreOperation::Verify,
            )
        })
    }

    /// Try read and verify a value without returning it.
    ///
    /// # Errors
    ///
    /// Returns an error if the verifier function returned `false` or an error.
    pub fn try_verify<F>(&mut self, expected: &'static str, verifier: F) -> Result<(), E>
    where
        F: FnOnce(&mut Self) -> Result<bool, E>,
        E: WithContext<'i>,
        E: From<ExpectedValid<'i>>,
    {
        self.try_advance(|input| {
            input.try_split_expect(
                |r: &mut Self| match verifier(r) {
                    Ok(true) => Ok(Some(())),
                    Ok(false) => Ok(None),
                    Err(err) => Err(err),
                },
                expected,
                CoreOperation::Verify,
            )
        })
    }

    /// Expect a value to be read and returned as `Some(T)`.
    ///
    /// # Errors
    ///
    /// Returns an error if the returned value was `None`.
    pub fn expect<F, T>(&mut self, expected: &'static str, f: F) -> Result<T, E>
    where
        F: FnOnce(&mut Self) -> Option<T>,
        E: From<ExpectedValid<'i>>,
    {
        self.try_advance(|input| input.split_expect(f, expected, CoreOperation::Expect))
    }

    /// Expect a value to be read successfully and returned as `Some(T)`.
    ///
    /// # Errors
    ///
    /// Returns an error if the returned value was `None` or if the provided
    /// function does.
    pub fn try_expect<F, T>(&mut self, expected: &'static str, f: F) -> Result<T, E>
    where
        E: WithContext<'i>,
        E: From<ExpectedValid<'i>>,
        F: FnOnce(&mut Self) -> Result<Option<T>, E>,
    {
        self.try_advance(|input| input.try_split_expect(f, expected, CoreOperation::Expect))
    }

    /// Tries to read an expected value with support for an external error.
    ///
    /// This function is useful for reading custom/unsupported types easily
    /// without having to create custom errors.
    ///
    /// # Usage
    ///
    /// On success, the provided function must return `Ok((T, usize))` where `T`
    /// is the type being read and `usize` being the length in bytes of input
    /// that was consumed. The length of bytes returned **MUST** align to a
    /// token boundary within the input, else an error will be returned. For a
    /// [`BytesReader`] this doesn't matter, but for a [`StringReader`] the
    /// length returned must sit on a valid char boundary.
    ///
    /// # Example
    ///
    /// ```
    /// use dangerous::{BytesReader, Error, Expected, Input};
    ///
    /// // Our custom reader function
    /// fn read_number<'i, E>(r: &mut BytesReader<'i, E>) -> Result<u32, E>
    /// where
    ///   E: Error<'i>,
    /// {
    ///     r.take_remaining_str()?.read_all(|r| {
    ///         r.try_expect_external("number", |i| {
    ///             // We map the parsed number along with the byte length
    ///             // of the input to tell the reader we read all of it.
    ///             i.as_dangerous().parse().map(|number| (i.byte_len(), number))
    ///         })
    ///     })
    /// }
    ///
    /// let input = dangerous::input(b"12x");
    /// let error: Expected<'_> = input.read_all(read_number).unwrap_err();
    ///
    /// // Prefer string input formatting
    /// println!("{:#}", error);
    /// ```
    ///
    /// # Errors
    ///
    /// Returns [`ExpectedValid`] if:
    ///
    /// - the provided function returns an amount of input read not aligned to a
    ///   token boundary
    /// - the provided function returns an [`External`] error.
    ///
    /// Returns [`ExpectedLength`] if:
    ///
    /// - the provided function returns an amount of input read that is greater
    ///   than the actual length
    ///
    /// [`BytesReader`]: crate::BytesReader  
    /// [`StringReader`]: crate::StringReader
    pub fn try_expect_external<F, T, R>(&mut self, expected: &'static str, f: F) -> Result<T, E>
    where
        E: WithContext<'i>,
        E: From<ExpectedValid<'i>>,
        E: From<ExpectedLength<'i>>,
        F: FnOnce(I) -> Result<(usize, T), R>,
        R: External<'i>,
    {
        self.try_advance(|input| {
            input.try_split_expect_external(f, expected, CoreOperation::ExpectExternal)
        })
    }

    /// Recovers from an error returning `Some(T)` if successful, or `None` if
    /// an error occurred.
    ///
    /// If an error is recovered from the `Reader`'s internal state is reset.
    #[inline]
    pub fn recover<F, T>(&mut self, f: F) -> Option<T>
    where
        F: FnOnce(&mut Self) -> Result<T, E>,
    {
        let checkpoint = self.input.clone();
        if let Ok(ok) = f(self) {
            Some(ok)
        } else {
            self.input = checkpoint;
            None
        }
    }

    /// Recovers from an error based on a predicate.
    ///
    /// If an error is recovered from the `Reader`'s internal state is reset.
    ///
    /// If an error occurs and the predicate returns `true` the error is
    /// recovered, `Ok(None)` is returned.
    ///
    /// # Errors
    ///
    /// If an error occurs and the predicate returns `false` the error is not
    /// recovered, `Err(E)` is returned.
    #[inline]
    pub fn recover_if<F, T, R>(&mut self, f: F, pred: R) -> Result<Option<T>, E>
    where
        E: WithContext<'i>,
        F: FnOnce(&mut Self) -> Result<T, E>,
        R: FnOnce(&E) -> bool,
    {
        let checkpoint = self.input.clone();
        match f(self) {
            Ok(ok) => Ok(Some(ok)),
            Err(err) => {
                if pred(&err) {
                    self.input = checkpoint;
                    Ok(None)
                } else {
                    Err(err
                        .with_context(CoreContext::from_operation(
                            CoreOperation::RecoverIf,
                            checkpoint.span(),
                        ))
                        .with_input(checkpoint))
                }
            }
        }
    }

    /// Read with a different error type.
    ///
    /// Keep in mind using different errors types can increase your binary size,
    /// use sparingly.
    ///
    /// # Example
    ///
    /// ```
    /// use dangerous::{BytesReader, Error, Expected, Fatal, Input};
    ///
    /// fn branch_a<'i, E>(r: &mut BytesReader<'i, E>) -> Result<u8, E>
    /// where
    ///     E: Error<'i>
    /// {
    ///     r.consume(b"hello").map(|()| 1)
    /// }
    ///
    /// fn branch_b<'i, E>(r: &mut BytesReader<'i, E>) -> Result<u8, E>
    /// where
    ///     E: Error<'i>
    /// {
    ///     r.consume(b"world").map(|()| 2)
    /// }
    ///
    /// let input = dangerous::input(b"world");
    /// let result: Result<_, Expected<'_>> = input.read_all(|r| {
    ///     r.expect("valid branch", |r| {
    ///         r.error(|r: &mut BytesReader<'_, Fatal>| {
    ///             r.recover(branch_a).or_else(|| r.recover(branch_b))
    ///         })
    ///     })
    /// });
    ///
    /// assert_eq!(result.unwrap(), 2);
    /// ```
    #[inline]
    pub fn error<F, T, S>(&mut self, f: F) -> T
    where
        F: FnOnce(&mut Reader<'i, I, S>) -> T,
    {
        self.advance(|input| {
            let mut sub = Reader::new(input);
            let ok = f(&mut sub);
            (ok, sub.input)
        })
    }

    /// Read a length of input.
    ///
    /// # Errors
    ///
    /// Returns an error if the length requirement to read could not be met.
    pub fn take(&mut self, len: usize) -> Result<I, E>
    where
        E: From<ExpectedLength<'i>>,
    {
        self.try_advance(|input| input.split_at(len, CoreOperation::Take))
    }

    /// Read an optional length of input.
    ///
    /// Returns `Some(I)` if there was enough input, `None` if not.
    pub fn take_opt(&mut self, len: usize) -> Option<I> {
        self.advance_opt(|input| input.split_at_opt(len))
    }

    /// Read a length of input while a pattern matches.
    ///
    /// Returns the input leading up to when the predicate returns `false`.
    ///
    /// # Example
    ///
    /// ```
    /// use dangerous::{Input, Invalid};
    ///
    /// let result: Result<_, Invalid> = dangerous::input(b"hello!").read_all(|r| {
    ///     r.take_while(|b: u8| b.is_ascii_alphabetic());
    ///     r.consume(b'!')
    /// });
    ///
    /// assert!(result.is_ok());
    /// ```
    pub fn take_while<P>(&mut self, pattern: P) -> I
    where
        P: Pattern<I>,
    {
        self.advance(|input| match input.clone().split_while_opt(pattern) {
            Some((taken, next)) => (taken, next),
            None => (input.clone(), input.end()),
        })
    }

    /// Try read a length of input while a predicate check remains successful
    /// and true.
    ///
    /// # Errors
    ///
    /// Returns any error the provided function does.
    pub fn try_take_while<F>(&mut self, pred: F) -> Result<I, E>
    where
        E: WithContext<'i>,
        F: FnMut(I::Token) -> Result<bool, E>,
    {
        self.try_advance(|input| input.try_split_while(pred, CoreOperation::TakeWhile))
    }

    /// Read a length of input until a expected pattern matches.
    ///
    /// Returns the input leading up to the pattern match.
    ///
    /// # Example
    ///
    /// ```
    /// use dangerous::{Input, Invalid};
    ///
    /// let result: Result<_, Invalid> = dangerous::input(b"hello world").read_all(|r| {
    ///     let before_space = r.take_until(b' ')?;
    ///     Ok((before_space, r.take_remaining()))
    /// });
    ///
    /// let (before_space, remaining) = result.unwrap();
    ///
    /// assert_eq!(before_space, b"hello"[..]);
    /// assert_eq!(remaining, b" world"[..]);
    /// ```
    ///
    /// # Errors
    ///
    /// Returns [`ExpectedValue`] if the pattern could not be found.
    pub fn take_until<P>(&mut self, pattern: P) -> Result<I, E>
    where
        E: From<ExpectedValue<'i>>,
        P: Pattern<I> + Into<Value<'i>> + Copy,
    {
        self.try_advance(|input| input.split_until(pattern, CoreOperation::TakeUntil))
    }

    /// Read a length of input until a pattern matches and consumes the matched
    /// input if found.
    ///
    /// Returns the input leading up to the pattern match.
    ///
    /// # Example
    ///
    /// ```
    /// use dangerous::{Input, Invalid};
    ///
    /// let result: Result<_, Invalid> = dangerous::input(b"hello world").read_all(|r| {
    ///     let before_space = r.take_until_consume(b' ')?;
    ///     Ok((before_space, r.take_remaining()))
    /// });
    ///
    /// let (before_space, remaining) = result.unwrap();
    ///
    /// assert_eq!(before_space, b"hello"[..]);
    /// assert_eq!(remaining, b"world"[..]);
    /// ```
    ///
    /// # Errors
    ///
    /// Returns [`ExpectedValue`] if the pattern could not be found.
    pub fn take_until_consume<P>(&mut self, pattern: P) -> Result<I, E>
    where
        E: From<ExpectedValue<'i>>,
        P: Pattern<I> + Into<Value<'i>> + Copy,
    {
        self.try_advance(|input| {
            input.split_until_consume(pattern, CoreOperation::TakeUntilConsume)
        })
    }

    /// Read a length of input until a pattern optionally matches.
    ///
    /// If you want to know whether the pattern was consumed or not, check
    /// [`Reader::at_end()`]. If the reader is at the end, then the pattern was
    /// not consumed.
    ///
    /// Returns the input leading up to the pattern match if any.
    pub fn take_until_opt<P>(&mut self, pattern: P) -> I
    where
        P: Pattern<I>,
    {
        self.advance(|input| match input.clone().split_until_opt(pattern) {
            Some((taken, next)) => (taken, next),
            None => (input.clone(), input.end()),
        })
    }

    /// Read a length of input until a pattern optionally matches and consumes
    /// the matched input if found.
    ///
    /// Returns a tuple with:
    ///
    /// - The input leading up to the pattern match if any.
    /// - `true` if the pattern was consumed, `false` if not.
    pub fn take_until_consume_opt<P>(&mut self, pattern: P) -> (I, bool)
    where
        P: Pattern<I>,
    {
        self.advance(
            |input| match input.clone().split_until_consume_opt(pattern) {
                Some((taken, next)) => ((taken, true), next),
                None => ((input.clone(), false), input.end()),
            },
        )
    }

    /// Skip `len` number of tokens.
    ///
    /// # Errors
    ///
    /// Returns an error if the length requirement to skip could not be met.
    #[inline]
    pub fn skip(&mut self, len: usize) -> Result<(), E>
    where
        E: From<ExpectedLength<'i>>,
    {
        self.try_advance(|input| input.split_at(len, CoreOperation::Skip))
            .map(drop)
    }

    /// Skip an optional length of input.
    ///
    /// Returns `true` if there was enough input, `false` if not.
    pub fn skip_opt(&mut self, len: usize) -> bool {
        self.advance_opt(|input| input.split_at_opt(len)).is_some()
    }

    /// Skip a length of input while a pattern matches.
    pub fn skip_while<P>(&mut self, pattern: P)
    where
        P: Pattern<I>,
    {
        let _skipped = self.take_while(pattern);
    }

    /// Try skip a length of input while a predicate check remains successful
    /// and true.
    ///
    /// # Errors
    ///
    /// Returns any error the provided function does.
    pub fn try_skip_while<F>(&mut self, pred: F) -> Result<(), E>
    where
        E: WithContext<'i>,
        F: FnMut(I::Token) -> Result<bool, E>,
    {
        self.try_advance(|input| input.try_split_while(pred, CoreOperation::SkipWhile))
            .map(drop)
    }

    /// Skip a length of input until a expected pattern matches.
    ///
    /// # Errors
    ///
    /// Returns [`ExpectedValue`] if the pattern could not be found.
    pub fn skip_until<P>(&mut self, pattern: P) -> Result<(), E>
    where
        E: From<ExpectedValue<'i>>,
        P: Pattern<I> + Into<Value<'i>> + Copy,
    {
        self.try_advance(|input| input.split_until(pattern, CoreOperation::SkipUntil))
            .map(drop)
    }

    /// Skip a length of input until a pattern matches and consumes the matched
    /// input if found.
    ///
    /// # Errors
    ///
    /// Returns [`ExpectedValue`] if the pattern could not be found.
    pub fn skip_until_consume<P>(&mut self, pattern: P) -> Result<(), E>
    where
        E: From<ExpectedValue<'i>>,
        P: Pattern<I> + Into<Value<'i>> + Copy,
    {
        self.try_advance(|input| {
            input.split_until_consume(pattern, CoreOperation::SkipUntilConsume)
        })
        .map(drop)
    }

    /// Skip a length of input until a pattern optionally matches.
    pub fn skip_until_opt<P>(&mut self, pattern: P)
    where
        P: Pattern<I>,
    {
        let _skipped = self.take_until_opt(pattern);
    }

    /// Skip a length of input until a pattern optionally matches and consumes
    /// the matched input if found.
    ///
    /// Returns `true` if the pattern was consumed, `false` if not.
    pub fn skip_until_consume_opt<P>(&mut self, pattern: P) -> bool
    where
        P: Pattern<I>,
    {
        let (_skipped, consumed) = self.take_until_consume_opt(pattern);
        consumed
    }

    /// Consume expected input.
    ///
    /// Doesn't effect the internal state of the `Reader` if the input couldn't
    /// be consumed.
    ///
    /// # Errors
    ///
    /// Returns an error if the input could not be consumed.
    pub fn consume<P>(&mut self, prefix: P) -> Result<(), E>
    where
        E: From<ExpectedValue<'i>>,
        P: Prefix<I> + Into<Value<'i>>,
    {
        self.try_advance(|input| input.split_prefix(prefix, CoreOperation::Consume))
            .map(drop)
    }

    /// Consume optional input.
    ///
    /// Returns `true` if the input was consumed, `false` if not.
    ///
    /// Doesn't effect the internal state of the `Reader` if the input couldn't
    /// be consumed.
    pub fn consume_opt<P>(&mut self, prefix: P) -> bool
    where
        P: Prefix<I>,
    {
        self.advance(|input| {
            let (prefix, next) = input.split_prefix_opt(prefix);
            (prefix.is_some(), next)
        })
    }

    /// Peek a length of input.
    ///
    /// The function lifetime `'p` helps prevent the peeked [`Input`] being used
    /// as a value in a parsed structure. Peeked values should only be used in
    /// choosing a correct parse path.
    ///
    /// # Errors
    ///
    /// Returns an error if the length requirement to peek could not be met.
    pub fn peek<'p>(&'p self, len: usize) -> Result<Peek<'p, I>, E>
    where
        E: From<ExpectedLength<'i>>,
    {
        match self.input.clone().split_at(len, CoreOperation::Peek) {
            Ok((head, _)) => Ok(Peek::new(head)),
            Err(err) => Err(err),
        }
    }

    /// Peek a length of input.
    ///
    /// This is equivalent to `peek` but does not return an error. Don't use
    /// this function if you want an error if there isn't enough input.
    #[must_use = "peek result must be used"]
    #[allow(clippy::needless_lifetimes)]
    pub fn peek_opt<'p>(&'p self, len: usize) -> Option<Peek<'p, I>> {
        self.input
            .clone()
            .split_at_opt(len)
            .map(|(head, _)| Peek::new(head))
    }

    /// Returns `true` if `prefix` is next in the `Reader`.
    #[inline]
    #[must_use = "peek result must be used"]
    pub fn peek_eq<P>(&self, prefix: P) -> bool
    where
        P: Prefix<I>,
    {
        prefix.is_prefix_of(&self.input)
    }
}