use core::any::Any;
use crate::fmt;
use crate::input::{Input, MaybeString, Span};
use super::WithContext;
pub trait Context: 'static + Send + Sync {
fn span(&self) -> Option<Span> {
None
}
fn operation(&self) -> &dyn Operation;
fn has_expected(&self) -> bool {
false
}
fn expected(&self, _w: &mut dyn fmt::Write) -> fmt::Result {
Err(fmt::Error)
}
fn is_child(&self) -> bool {
false
}
}
pub trait Operation: Any + Send + Sync {
fn description(&self, w: &mut dyn fmt::Write) -> fmt::Result;
fn as_any(&self) -> &dyn Any;
}
impl Context for &'static str {
fn operation(&self) -> &dyn Operation {
&CoreOperation::Context
}
fn has_expected(&self) -> bool {
true
}
fn expected(&self, w: &mut dyn fmt::Write) -> fmt::Result {
w.write_str(self)
}
}
impl Operation for &'static str {
fn description(&self, w: &mut dyn fmt::Write) -> fmt::Result {
w.write_str(self)
}
fn as_any(&self) -> &dyn Any {
self
}
}
#[derive(Copy, Clone)]
pub struct ExternalContext<O, E> {
pub operation: Option<O>,
pub expected: Option<E>,
}
impl<O, E> Context for ExternalContext<O, E>
where
O: Operation,
E: fmt::DisplayBase + Send + Sync + 'static,
{
fn operation(&self) -> &dyn Operation {
match &self.operation {
None => &CoreOperation::Context,
Some(operation) => operation,
}
}
fn has_expected(&self) -> bool {
self.expected.is_some()
}
fn expected(&self, w: &mut dyn fmt::Write) -> fmt::Result {
match &self.expected {
None => Err(fmt::Error),
Some(expected) => expected.fmt(w),
}
}
}
impl<O, E> fmt::Debug for ExternalContext<O, E>
where
O: fmt::Debug,
E: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ExternalContext")
.field("operation", &self.operation)
.field("expected", &self.expected)
.finish()
}
}
#[non_exhaustive]
#[derive(Copy, Clone)]
pub struct CoreContext {
pub span: Span,
pub operation: CoreOperation,
pub expected: CoreExpected,
}
impl CoreContext {
pub(crate) fn from_operation(operation: CoreOperation, span: Span) -> Self {
Self {
span,
operation,
expected: CoreExpected::Unknown,
}
}
pub fn debug_for(self, input: MaybeString<'_>) -> DebugFor<'_> {
DebugFor {
input,
context: self,
}
}
}
impl Context for CoreContext {
fn span(&self) -> Option<Span> {
Some(self.span)
}
fn operation(&self) -> &dyn Operation {
&self.operation
}
fn has_expected(&self) -> bool {
self.expected != CoreExpected::Unknown
}
fn expected(&self, w: &mut dyn fmt::Write) -> fmt::Result {
fmt::DisplayBase::fmt(&self.expected, w)
}
}
impl fmt::Debug for CoreContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CoreContext")
.field("span", &self.span)
.field("operation", &self.operation)
.field("expected", &self.expected)
.finish()
}
}
#[must_use]
pub struct DebugFor<'i> {
input: MaybeString<'i>,
context: CoreContext,
}
impl<'i> fmt::Debug for DebugFor<'i> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CoreContext")
.field("span", &self.context.span.debug_for(self.input.clone()))
.field("operation", &self.context.operation)
.field("expected", &self.context.expected)
.finish()
}
}
#[allow(missing_docs)]
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum CoreOperation {
Context,
ReadAll,
ReadPartial,
Consume,
Skip,
SkipWhile,
SkipStrWhile,
SkipUntil,
SkipUntilConsume,
Take,
TakeUntil,
TakeUntilConsume,
TakeWhile,
TakeConsumed,
TakeStrWhile,
TakeRemainingStr,
Peek,
PeekU8,
PeekChar,
ReadU8,
ReadChar,
ReadArray,
RecoverIf,
Verify,
Expect,
ExpectExternal,
IntoNonEmpty,
IntoExternal,
IntoString,
}
impl Operation for CoreOperation {
fn description(&self, w: &mut dyn fmt::Write) -> fmt::Result {
w.write_str(CoreOperation::description(*self))
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl CoreOperation {
fn description(self) -> &'static str {
match self {
Self::Context => "<context>",
Self::ReadAll => "read all input",
Self::ReadPartial => "read a partial length of input",
Self::Consume => "consume input",
Self::Skip => "skip a length of input",
Self::SkipWhile => "skip input while a pattern matches",
Self::SkipUntil => "skip input until a pattern matches",
Self::SkipUntilConsume => "skip input until a pattern matches and consume it",
Self::SkipStrWhile => "skip UTF-8 input while a condition remains true",
Self::Take => "take a length of input",
Self::TakeWhile => "take input while a pattern matches",
Self::TakeUntil => "take input until a pattern matches",
Self::TakeUntilConsume => "take input until a pattern matches and consume it",
Self::TakeConsumed => "take input that was consumed",
Self::TakeStrWhile => "take UTF-8 input while a condition remains true",
Self::TakeRemainingStr => "take remaining string within bytes",
Self::Peek => "peek a length of input",
Self::PeekU8 => "peek a u8",
Self::PeekChar => "peek a char",
Self::ReadU8 => "read a u8",
Self::ReadChar => "read a char",
Self::ReadArray => "read an array of bytes",
Self::RecoverIf => "recover if a condition returns true",
Self::Verify => "read and verify input",
Self::Expect => "read and expect a value",
Self::ExpectExternal => "read and expect an external value",
Self::IntoNonEmpty => "convert input into non-empty input",
Self::IntoExternal => "convert input into external type",
Self::IntoString => "convert input into string",
}
}
}
impl fmt::DisplayBase for CoreOperation {
fn fmt(&self, w: &mut dyn fmt::Write) -> fmt::Result {
self.description(w)
}
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum CoreExpected {
Unknown,
NonEmpty,
ExactValue,
PatternMatch,
NoTrailingInput,
Valid(&'static str),
EnoughInputFor(&'static str),
}
impl fmt::DisplayBase for CoreExpected {
fn fmt(&self, w: &mut dyn fmt::Write) -> fmt::Result {
match *self {
Self::Unknown => w.write_str("unknown"),
Self::NonEmpty => w.write_str("non-empty input"),
Self::ExactValue => w.write_str("exact value"),
Self::PatternMatch => w.write_str("pattern match"),
Self::NoTrailingInput => w.write_str("no trailing input"),
Self::Valid(expected) => w.write_str(expected),
Self::EnoughInputFor(expected) => {
w.write_str("enough input for ")?;
w.write_str(expected)
}
}
}
}
pub struct WithChildContext<E>(E);
impl<E> WithChildContext<E> {
pub fn new(inner: E) -> Self {
Self(inner)
}
pub fn unwrap(self) -> E {
self.0
}
}
impl<'i, E> WithContext<'i> for WithChildContext<E>
where
E: WithContext<'i>,
{
const PASSTHROUGH: bool = E::PASSTHROUGH;
#[inline(always)]
fn with_input(self, _input: impl Input<'i>) -> Self {
self
}
#[inline(always)]
fn with_context(self, context: impl Context) -> Self {
Self(self.0.with_context(ChildContext(context)))
}
}
struct ChildContext<T>(T);
impl<T> Context for ChildContext<T>
where
T: Context,
{
fn operation(&self) -> &dyn Operation {
self.0.operation()
}
fn has_expected(&self) -> bool {
self.0.has_expected()
}
fn expected(&self, w: &mut dyn fmt::Write) -> fmt::Result {
self.0.expected(w)
}
fn is_child(&self) -> bool {
true
}
}
#[inline(always)]
pub(crate) fn with_context<'i, F, T, E>(
context: impl Context,
input: impl Input<'i>,
f: F,
) -> Result<T, E>
where
E: WithContext<'i>,
F: FnOnce() -> Result<T, E>,
{
match f() {
Ok(ok) => Ok(ok),
Err(err) => Err(err.with_context(context).with_input(input)),
}
}