use core::{
convert::Infallible,
fmt::{
self,
Debug,
Display,
Formatter,
},
ops::{
ControlFlow,
FromResidual,
Try,
},
};
use crate::{
Contexting,
ParsedAux,
ProvideElement,
Split,
Streaming,
Success,
};
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Parsed<Token, Stream, Context> {
Success {
token: Token,
stream: Stream,
},
Failure(Context),
Error(Context),
}
impl<Token, Stream, Context> Parsed<Token, Stream, Context> {
pub const fn new_success(token: Token, stream: Stream) -> Self {
Self::Success { token, stream }
}
pub const fn new_failure(content: Context) -> Self {
Self::Failure(content)
}
pub const fn new_error(content: Context) -> Self {
Self::Error(content)
}
pub const fn as_ref(&self) -> Parsed<&Token, &Stream, &Context> {
match self {
Self::Success { token, stream } => Parsed::Success { token, stream },
Self::Failure(context) => Parsed::Failure(context),
Self::Error(context) => Parsed::Error(context),
}
}
pub fn map_success<MappedToken, Map>(self, map: Map) -> Parsed<MappedToken, Stream, Context>
where
Map: FnOnce(Success<Token, Stream>) -> Success<MappedToken, Stream>,
{
match self {
Parsed::Success { token, stream } => map(Success { token, stream }).into(),
Parsed::Failure(context) => Parsed::Failure(context),
Parsed::Error(context) => Parsed::Error(context),
}
}
pub fn map_token<MappedToken, Map>(self, map: Map) -> Parsed<MappedToken, Stream, Context>
where
Map: FnOnce(Token) -> MappedToken,
{
match self {
Parsed::Success { token, stream } => Parsed::Success {
token: map(token),
stream,
},
Parsed::Failure(context) => Parsed::Failure(context),
Parsed::Error(context) => Parsed::Error(context),
}
}
pub fn map_stream<Map>(self, map: Map) -> Parsed<Token, Stream, Context>
where
Map: FnOnce(Stream) -> Stream,
{
match self {
Parsed::Success { token, stream } => Parsed::Success {
token,
stream: map(stream),
},
Parsed::Failure(context) => Parsed::Failure(context),
Parsed::Error(context) => Parsed::Error(context),
}
}
pub fn map_context<MappedAtom, Map>(self, map: Map) -> Parsed<Token, Stream, MappedAtom>
where
Map: FnOnce(Context) -> MappedAtom,
{
match self {
Parsed::Success { token, stream } => Parsed::Success { token, stream },
Parsed::Failure(context) => Parsed::Failure(map(context)),
Parsed::Error(context) => Parsed::Error(map(context)),
}
}
pub fn add_context<C, Map>(self, map: Map) -> Parsed<Token, Stream, Context>
where
Map: FnOnce() -> C,
Context: Contexting<C>,
{
match self {
Parsed::Success { token, stream } => Parsed::Success { token, stream },
Parsed::Failure(content) => Parsed::Failure(content.add(map())),
Parsed::Error(content) => Parsed::Error(content.add(map())),
}
}
pub fn unwrap(self) -> Success<Token, Stream>
where
Context: Debug,
{
match self {
Parsed::Success { token, stream } => Success { token, stream },
Parsed::Failure(context) => panic!("Call unwrap on Parsed::Failure: {:?}", context),
Parsed::Error(context) => panic!("Call unwrap on Parsed::Error: {:?}", context),
}
}
pub fn unwrap_context(self) -> Context
where
Token: Debug,
Stream: Debug,
{
match self {
Parsed::Success { token, stream } => {
panic!("Call unwrap on Parsed::Success: {:?} {:?}", token, stream)
}
Parsed::Failure(context) => context,
Parsed::Error(context) => context,
}
}
pub const fn is_success(&self) -> bool {
match self {
Parsed::Success { .. } => true,
_ => false,
}
}
}
impl<Token, Stream, Context> From<Success<Token, Stream>> for Parsed<Token, Stream, Context> {
fn from(Success { token, stream }: Success<Token, Stream>) -> Self {
Parsed::Success { token, stream }
}
}
impl<Token, Stream, Context> FromResidual for Parsed<Token, Stream, Context> {
fn from_residual(residual: Parsed<Infallible, Infallible, Context>) -> Self {
match residual {
Parsed::Success { .. } => unreachable!(),
Parsed::Failure(context) => Parsed::Failure(context),
Parsed::Error(context) => Parsed::Error(context),
}
}
}
impl<Token, Stream, Context> FromResidual<ParsedAux<Infallible, Context>>
for Parsed<Token, Stream, Context>
{
fn from_residual(residual: ParsedAux<Infallible, Context>) -> Self {
match residual {
ParsedAux::Success(_success) => unreachable!(),
ParsedAux::Failure(context) => Parsed::Failure(context),
ParsedAux::Error(context) => Parsed::Error(context),
}
}
}
impl<Token, Stream, Context> FromResidual<Result<Infallible, Context>>
for Parsed<Token, Stream, Context>
{
fn from_residual(residual: Result<Infallible, Context>) -> Self {
match residual {
Ok(_success) => unreachable!(),
Err(context) => Parsed::Failure(context),
}
}
}
impl<Token, Stream, Context> Try for Parsed<Token, Stream, Context> {
type Output = Success<Token, Stream>;
type Residual = Parsed<Infallible, Infallible, Context>;
fn from_output(output: Self::Output) -> Self {
output.into()
}
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
match self {
Parsed::Success { token, stream } => ControlFlow::Continue(Success { token, stream }),
Parsed::Failure(context) => ControlFlow::Break(Parsed::Failure(context)),
Parsed::Error(context) => ControlFlow::Break(Parsed::Error(context)),
}
}
}
use owo_colors::OwoColorize;
impl<Token, Stream, Context> Display for Parsed<Token, Stream, Context>
where
Token: Debug,
Stream: Streaming,
Context: ProvideElement,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Parsed::Success { token, stream } => match stream.clone().split_at(8) {
Split::Success {
item: stream,
stream: _,
} => {
write!(
f,
"{}: {:02X?}, stream: {:02X?} ..",
"Success".green(),
token,
stream
)
}
Split::NotEnoughItem(stream) => {
write!(
f,
"{}: {:02X?}, stream: {:02X?}",
"Success".green(),
token,
stream
)
}
Split::Error(error) => {
write!(
f,
"{}: {:02X?}, stream: {:?}",
"Success".green(),
token,
error
)
}
},
Parsed::Failure(context) => {
write!(f, "{}: {}", "Failure".yellow(), context.last())
}
Parsed::Error(context) => {
write!(f, "{}: {}", "Error".red(), context.last())
}
}
}
}
#[cfg(test)]
mod tests {
use super::Parsed;
use crate::Success;
fn multiply_by_42(parsed: Parsed<u8, (), ()>) -> Parsed<u8, (), ()> {
let Success { token, .. } = parsed?;
Parsed::Success {
token: token * 42,
stream: (),
}
}
#[test]
fn parsed() {
assert_eq!(
multiply_by_42(Parsed::new_success(1, ())),
Parsed::new_success(42, ())
);
assert_eq!(multiply_by_42(Parsed::Failure(())), Parsed::Failure(()));
assert_eq!(multiply_by_42(Parsed::Error(())), Parsed::Error(()));
}
}