use core::fmt;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
use crate::display::{ByteCount, ErrorDisplay};
use crate::input::Input;
use super::{
Context, ContextStack, ContextStackBuilder, Details, ExpectedContext, FromContext, IntoFatal,
RetryRequirement, ToRetryRequirement,
};
#[cfg(feature = "full-context")]
type ExpectedContextStack = crate::error::FullContextStack;
#[cfg(not(feature = "full-context"))]
type ExpectedContextStack = crate::error::RootContextStack;
pub struct Expected<'i, S = ExpectedContextStack> {
input: Input<'i>,
stack: S,
fatal: bool,
kind: ExpectedKind<'i>,
}
enum ExpectedKind<'i> {
Value(ExpectedValue<'i>),
Valid(ExpectedValid<'i>),
Length(ExpectedLength<'i>),
}
impl<'i, S> Expected<'i, S>
where
S: ContextStack,
{
pub fn display(&self) -> ErrorDisplay<'_, Self> {
ErrorDisplay::new(self)
}
}
impl<'i, S> Expected<'i, S>
where
S: ContextStackBuilder,
{
fn add_context<C>(&mut self, input: Input<'i>, context: C)
where
C: Context,
{
if self.input.is_within(&input) {
self.input = input
}
self.stack.push(context);
}
fn from_kind(kind: ExpectedKind<'i>) -> Self {
let (input, context) = match &kind {
ExpectedKind::Valid(err) => (err.input(), err.context()),
ExpectedKind::Value(err) => (err.input(), err.context()),
ExpectedKind::Length(err) => (err.input(), err.context()),
};
Self {
kind,
input,
fatal: false,
stack: S::from_root(context),
}
}
}
impl<'i, S> Details<'i> for Expected<'i, S>
where
S: ContextStack,
{
fn input(&self) -> Input<'i> {
self.input.clone()
}
fn span(&self) -> Input<'i> {
match &self.kind {
ExpectedKind::Value(err) => err.found(),
ExpectedKind::Valid(err) => err.span(),
ExpectedKind::Length(err) => err.span(),
}
}
fn expected(&self) -> Option<Input<'_>> {
match &self.kind {
ExpectedKind::Value(err) => Some(err.expected()),
ExpectedKind::Valid(_) | ExpectedKind::Length(_) => None,
}
}
fn description(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.kind {
ExpectedKind::Value(err) => fmt::Display::fmt(err, f),
ExpectedKind::Valid(err) => fmt::Display::fmt(err, f),
ExpectedKind::Length(err) => fmt::Display::fmt(err, f),
}
}
fn context_stack(&self) -> &dyn ContextStack {
&self.stack
}
}
impl<'i, S> ToRetryRequirement for Expected<'i, S> {
fn to_retry_requirement(&self) -> Option<RetryRequirement> {
match &self.kind {
ExpectedKind::Value(err) => err.to_retry_requirement(),
ExpectedKind::Valid(err) => err.to_retry_requirement(),
ExpectedKind::Length(err) => err.to_retry_requirement(),
}
}
fn is_fatal(&self) -> bool {
if self.fatal {
return true;
}
match &self.kind {
ExpectedKind::Value(err) => err.is_fatal(),
ExpectedKind::Valid(err) => err.is_fatal(),
ExpectedKind::Length(err) => err.is_fatal(),
}
}
}
#[cfg(feature = "alloc")]
impl<'i, S> ToRetryRequirement for Box<Expected<'i, S>> {
fn to_retry_requirement(&self) -> Option<RetryRequirement> {
(**self).to_retry_requirement()
}
fn is_fatal(&self) -> bool {
(**self).is_fatal()
}
}
impl<'i, S> IntoFatal for Expected<'i, S> {
fn into_fatal(mut self) -> Self {
self.fatal = true;
self
}
}
#[cfg(feature = "alloc")]
impl<'i, S> IntoFatal for Box<Expected<'i, S>> {
fn into_fatal(mut self) -> Self {
self.fatal = true;
self
}
}
impl<'i, S> FromContext<'i> for Expected<'i, S>
where
S: ContextStackBuilder,
{
fn from_context<C>(mut self, input: Input<'i>, context: C) -> Self
where
C: Context,
{
self.add_context(input, context);
self
}
}
#[cfg(feature = "alloc")]
impl<'i, S> FromContext<'i> for Box<Expected<'i, S>>
where
S: ContextStackBuilder,
{
fn from_context<C>(mut self, input: Input<'i>, context: C) -> Self
where
C: Context,
{
self.add_context(input, context);
self
}
}
impl<'i, S> fmt::Display for Expected<'i, S>
where
S: ContextStack,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
ErrorDisplay::from_formatter(self, f).fmt(f)
}
}
impl<'i, S> fmt::Debug for Expected<'i, S>
where
S: ContextStack,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
ErrorDisplay::from_formatter(self, f).banner(true).fmt(f)
}
}
impl<'i, S> From<ExpectedLength<'i>> for Expected<'i, S>
where
S: ContextStackBuilder,
{
fn from(err: ExpectedLength<'i>) -> Self {
Self::from_kind(ExpectedKind::Length(err))
}
}
#[cfg(feature = "alloc")]
impl<'i, S> From<ExpectedLength<'i>> for Box<Expected<'i, S>>
where
S: ContextStackBuilder,
{
fn from(expected: ExpectedLength<'i>) -> Box<Expected<'i, S>> {
Box::new(expected.into())
}
}
impl<'i, S> From<ExpectedValid<'i>> for Expected<'i, S>
where
S: ContextStackBuilder,
{
fn from(err: ExpectedValid<'i>) -> Self {
Self::from_kind(ExpectedKind::Valid(err))
}
}
#[cfg(feature = "alloc")]
impl<'i, S> From<ExpectedValid<'i>> for Box<Expected<'i, S>>
where
S: ContextStackBuilder,
{
fn from(expected: ExpectedValid<'i>) -> Box<Expected<'i, S>> {
Box::new(expected.into())
}
}
impl<'i, S> From<ExpectedValue<'i>> for Expected<'i, S>
where
S: ContextStackBuilder,
{
fn from(err: ExpectedValue<'i>) -> Self {
Self::from_kind(ExpectedKind::Value(err))
}
}
#[cfg(feature = "alloc")]
impl<'i, S> From<ExpectedValue<'i>> for Box<Expected<'i, S>>
where
S: ContextStackBuilder,
{
fn from(expected: ExpectedValue<'i>) -> Box<Expected<'i, S>> {
Box::new(expected.into())
}
}
#[cfg(feature = "std")]
impl<'i, S> std::error::Error for Expected<'i, S> where S: ContextStack {}
#[derive(Debug)]
pub struct ExpectedValue<'i> {
pub(crate) input: Input<'i>,
pub(crate) actual: &'i [u8],
pub(crate) expected: &'i [u8],
pub(crate) context: ExpectedContext,
}
impl<'i> ExpectedValue<'i> {
#[inline(always)]
pub fn input(&self) -> Input<'i> {
self.input.clone()
}
#[inline(always)]
pub fn context(&self) -> ExpectedContext {
self.context
}
#[inline(always)]
pub fn found(&self) -> Input<'i> {
Input::new(self.actual, self.input.is_bound())
}
#[inline(always)]
pub fn expected(&self) -> Input<'i> {
Input::new(self.expected, self.input.is_bound())
}
}
impl<'i> fmt::Display for ExpectedValue<'i> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("found a different value to the exact expected")
}
}
impl<'i> ToRetryRequirement for ExpectedValue<'i> {
#[inline]
fn to_retry_requirement(&self) -> Option<RetryRequirement> {
if self.is_fatal() {
None
} else {
let needed = self.expected().len();
let had = self.found().len();
RetryRequirement::from_had_and_needed(had, needed)
}
}
#[inline]
fn is_fatal(&self) -> bool {
self.input.is_bound() || !self.expected().has_prefix(self.found().as_dangerous())
}
}
#[derive(Debug)]
pub struct ExpectedLength<'i> {
pub(crate) min: usize,
pub(crate) max: Option<usize>,
pub(crate) span: &'i [u8],
pub(crate) input: Input<'i>,
pub(crate) context: ExpectedContext,
}
impl<'i> ExpectedLength<'i> {
#[inline(always)]
pub fn input(&self) -> Input<'i> {
self.input.clone()
}
#[inline(always)]
pub fn context(&self) -> ExpectedContext {
self.context
}
#[inline(always)]
pub fn span(&self) -> Input<'i> {
Input::new(self.span, self.input.is_bound())
}
#[inline(always)]
pub fn min(&self) -> usize {
self.min
}
#[inline(always)]
pub fn max(&self) -> Option<usize> {
self.max
}
#[inline]
pub fn is_exact(&self) -> bool {
Some(self.min) == self.max
}
#[inline]
pub fn exact(&self) -> Option<usize> {
if self.is_exact() {
self.max
} else {
None
}
}
}
impl<'i> fmt::Display for ExpectedLength<'i> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "found {} when ", ByteCount(self.span().len()))?;
match (self.min(), self.max()) {
(0, Some(max)) => write!(f, "at most {}", ByteCount(max)),
(min, None) => write!(f, "at least {}", ByteCount(min)),
(min, Some(max)) if min == max => write!(f, "exactly {}", ByteCount(min)),
(min, Some(max)) => write!(
f,
"at least {} and at most {}",
ByteCount(min),
ByteCount(max)
),
}?;
write!(f, " was expected")
}
}
impl<'i> ToRetryRequirement for ExpectedLength<'i> {
#[inline]
fn to_retry_requirement(&self) -> Option<RetryRequirement> {
if self.is_fatal() {
None
} else {
let had = self.span().len();
let needed = self.min();
RetryRequirement::from_had_and_needed(had, needed)
}
}
#[inline]
fn is_fatal(&self) -> bool {
self.input.is_bound() || self.max.is_some()
}
}
#[derive(Debug)]
pub struct ExpectedValid<'i> {
pub(crate) input: Input<'i>,
pub(crate) span: &'i [u8],
pub(crate) context: ExpectedContext,
pub(crate) retry_requirement: Option<RetryRequirement>,
}
impl<'i> ExpectedValid<'i> {
#[inline(always)]
pub fn input(&self) -> Input<'i> {
self.input.clone()
}
#[inline(always)]
pub fn context(&self) -> ExpectedContext {
self.context
}
#[inline(always)]
pub fn span(&self) -> Input<'i> {
Input::new(self.span, self.input.is_bound())
}
#[inline(always)]
pub fn expected(&self) -> &'static str {
self.context.expected
}
}
impl<'i> fmt::Display for ExpectedValid<'i> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "expected {}", self.context.expected)
}
}
impl<'i> ToRetryRequirement for ExpectedValid<'i> {
#[inline]
fn to_retry_requirement(&self) -> Option<RetryRequirement> {
if self.is_fatal() {
None
} else {
self.retry_requirement
}
}
#[inline]
fn is_fatal(&self) -> bool {
self.input.is_bound() || self.retry_requirement.is_some()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(all(target_pointer_width = "64", not(feature = "full-context")))]
fn test_expected_size() {
assert_eq!(core::mem::size_of::<Expected<'_>>(), 168);
}
#[test]
#[cfg(all(target_pointer_width = "64", feature = "full-context"))]
fn test_expected_size() {
assert_eq!(core::mem::size_of::<Expected<'_>>(), 192);
}
}