use std::any::type_name;
use std::{fmt, mem};
use crate::{Decoder, MltError, MltResult};
pub trait Decode<Parsed>: Sized {
fn decode(self, decoder: &mut Decoder) -> MltResult<Parsed>;
}
mod sealed {
pub trait Sealed {}
}
pub trait DecodeState: sealed::Sealed {
type LazyOrParsed<Raw: fmt::Debug + Clone, Parsed: fmt::Debug + Clone>: fmt::Debug + Clone;
}
#[derive(Debug, Clone, PartialEq)]
pub struct Lazy;
#[derive(Debug, Clone, PartialEq)]
pub struct Parsed;
impl sealed::Sealed for Lazy {}
impl sealed::Sealed for Parsed {}
impl DecodeState for Lazy {
type LazyOrParsed<Raw: fmt::Debug + Clone, Parsed: fmt::Debug + Clone> =
LazyParsed<Raw, Parsed>;
}
impl DecodeState for Parsed {
type LazyOrParsed<Raw: fmt::Debug + Clone, Parsed: fmt::Debug + Clone> = Parsed;
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(all(not(test), feature = "arbitrary"), derive(arbitrary::Arbitrary))]
pub enum LazyParsed<Raw, Parsed> {
Raw(Raw),
Parsed(Parsed),
ParsingFailed,
}
impl<Raw: Decode<Parsed>, Parsed> LazyParsed<Raw, Parsed> {
pub fn decode(&mut self, decoder: &mut Decoder) -> MltResult<&mut Parsed> {
match self {
Self::Parsed(v) => Ok(v),
Self::Raw(_) => {
let Self::Raw(raw) = mem::replace(self, Self::ParsingFailed) else {
unreachable!();
};
*self = Self::Parsed(raw.decode(decoder)?);
let Self::Parsed(v) = self else {
unreachable!()
};
Ok(v)
}
Self::ParsingFailed => Err(MltError::PriorParseFailure),
}
}
pub fn into_parsed(self, decoder: &mut Decoder) -> MltResult<Parsed> {
match self {
Self::Parsed(v) => Ok(v),
Self::Raw(raw) => raw.decode(decoder),
Self::ParsingFailed => Err(MltError::PriorParseFailure),
}
}
pub fn as_parsed(&self) -> MltResult<&Parsed> {
match self {
Self::Parsed(v) => Ok(v),
Self::Raw(_) => Err(MltError::NotDecoded(type_name::<Parsed>())),
Self::ParsingFailed => Err(MltError::PriorParseFailure),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
struct RawWord(&'static str);
impl Decode<String> for RawWord {
fn decode(self, _decoder: &mut Decoder) -> MltResult<String> {
Ok(self.0.to_owned())
}
}
#[test]
fn as_parsed_on_raw_names_the_parsed_type() {
let lazy = LazyParsed::<RawWord, String>::Raw(RawWord("x"));
assert_eq!(
lazy.as_parsed().unwrap_err().to_string(),
"alloc::string::String is not decoded"
);
}
#[test]
fn as_parsed_on_parsed_returns_the_value() {
let lazy = LazyParsed::<RawWord, String>::Parsed("x".to_owned());
assert_eq!(lazy.as_parsed().unwrap(), "x");
}
#[test]
fn as_parsed_after_failure_reports_prior_failure() {
let lazy = LazyParsed::<RawWord, String>::ParsingFailed;
assert_eq!(
lazy.as_parsed().unwrap_err().to_string(),
"previous decode/parsing attempt failed"
);
}
}