use core::fmt;
#[cfg(feature = "box-expected")]
use alloc::boxed::Box;
use crate::display::{ByteCount, ErrorDisplay};
use crate::input::{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> {
#[cfg(feature = "box-expected")]
inner: Box<ExpectedInner<'i, S>>,
#[cfg(not(feature = "box-expected"))]
inner: ExpectedInner<'i, S>,
}
struct ExpectedInner<'i, S> {
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,
{
#[inline]
fn from_kind(kind: ExpectedKind<'i>) -> Self {
let context = match &kind {
ExpectedKind::Valid(err) => err.context(),
ExpectedKind::Value(err) => err.context(),
ExpectedKind::Length(err) => err.context(),
};
let inner = ExpectedInner {
kind,
fatal: false,
stack: S::from_root(context),
};
#[cfg(feature = "box-expected")]
let inner = Box::new(inner);
Self { inner }
}
}
impl<'i, S> Details<'i> for Expected<'i, S>
where
S: ContextStack,
{
fn input(&self) -> &'i Input {
match &self.inner.kind {
ExpectedKind::Value(err) => err.input(),
ExpectedKind::Valid(err) => err.input(),
ExpectedKind::Length(err) => err.input(),
}
}
fn span(&self) -> &'i Input {
match &self.inner.kind {
ExpectedKind::Value(err) => err.found(),
ExpectedKind::Valid(err) => err.span(),
ExpectedKind::Length(err) => err.span(),
}
}
fn expected(&self) -> Option<&Input> {
match &self.inner.kind {
ExpectedKind::Value(err) => Some(err.expected()),
ExpectedKind::Valid(_) | ExpectedKind::Length(_) => None,
}
}
fn description(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.inner.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.inner.stack
}
}
impl<'i, S> ToRetryRequirement for Expected<'i, S> {
fn to_retry_requirement(&self) -> Option<RetryRequirement> {
match &self.inner.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.inner.fatal {
return true;
}
match &self.inner.kind {
ExpectedKind::Value(err) => err.is_fatal(),
ExpectedKind::Valid(err) => err.is_fatal(),
ExpectedKind::Length(err) => err.is_fatal(),
}
}
}
impl<'i, S> IntoFatal for Expected<'i, S> {
fn into_fatal(mut self) -> Self {
self.inner.fatal = true;
self
}
}
impl<'i, S> FromContext<'i> for Expected<'i, S>
where
S: ContextStackBuilder,
{
fn from_context<C>(mut self, input: &'i Input, context: C) -> Self
where
C: Context,
{
let current_input = match &mut self.inner.kind {
ExpectedKind::Value(err) => &mut err.input,
ExpectedKind::Valid(err) => &mut err.input,
ExpectedKind::Length(err) => &mut err.input,
};
if current_input.is_within(input) {
*current_input = input
}
self.inner.stack.push(context);
self
}
}
impl<'i> fmt::Display for Expected<'i> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
ErrorDisplay::from_formatter(self, f).fmt(f)
}
}
impl<'i> fmt::Debug for Expected<'i> {
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))
}
}
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))
}
}
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 = "std")]
impl<'i> std::error::Error for Expected<'i> {}
#[derive(Debug, Clone)]
#[allow(variant_size_differences)]
pub(crate) enum Value<'a> {
Byte(u8),
Bytes(&'a [u8]),
}
impl<'i> Value<'i> {
pub(crate) fn as_input(&self) -> &Input {
match self {
Self::Byte(b) => Input::from_u8(b),
Self::Bytes(bytes) => input(bytes),
}
}
}
#[derive(Debug, Clone)]
pub struct ExpectedValue<'i> {
pub(crate) input: &'i Input,
pub(crate) actual: &'i Input,
pub(crate) expected: Value<'i>,
pub(crate) context: ExpectedContext,
}
impl<'i> ExpectedValue<'i> {
pub fn input(&self) -> &'i Input {
self.input
}
pub fn context(&self) -> ExpectedContext {
self.context
}
pub fn found(&self) -> &'i Input {
self.actual
}
pub fn expected(&self) -> &Input {
self.expected.as_input()
}
}
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> {
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)
}
}
fn is_fatal(&self) -> bool {
!self.expected().has_prefix(self.found().as_dangerous())
}
}
#[derive(Debug, Clone)]
pub struct ExpectedLength<'i> {
pub(crate) min: usize,
pub(crate) max: Option<usize>,
pub(crate) span: &'i Input,
pub(crate) input: &'i Input,
pub(crate) context: ExpectedContext,
}
impl<'i> ExpectedLength<'i> {
pub fn input(&self) -> &'i Input {
self.input
}
pub fn context(&self) -> ExpectedContext {
self.context
}
pub fn span(&self) -> &'i Input {
self.span
}
pub fn min(&self) -> usize {
self.min
}
pub fn max(&self) -> Option<usize> {
self.max
}
pub fn is_exact(&self) -> bool {
Some(self.min) == self.max
}
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> {
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)
}
}
fn is_fatal(&self) -> bool {
self.max.is_some()
}
}
#[derive(Debug, Clone)]
pub struct ExpectedValid<'i> {
pub(crate) input: &'i Input,
pub(crate) span: &'i Input,
pub(crate) context: ExpectedContext,
pub(crate) retry_requirement: Option<RetryRequirement>,
}
impl<'i> ExpectedValid<'i> {
pub fn input(&self) -> &'i Input {
self.input
}
pub fn context(&self) -> ExpectedContext {
self.context
}
pub fn span(&self) -> &'i Input {
self.span
}
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> {
fn to_retry_requirement(&self) -> Option<RetryRequirement> {
self.retry_requirement
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(all(target_pointer_width = "64", feature = "box-expected"))]
fn test_expected_size() {
assert_eq!(core::mem::size_of::<Expected<'_>>(), 8);
}
#[test]
#[cfg(all(target_pointer_width = "64", not(feature = "box-expected")))]
fn test_expected_size() {
assert_eq!(core::mem::size_of::<Expected<'_>>(), 136);
}
}