#[cfg(all(doc, not(feature = "std")))]
use alloc::vec::Vec;
use crate::{consts::TOTAL_PIECE_COUNT, replay::GameInputEvent};
use alloc::string::{FromUtf8Error, String};
use base64::DecodeError;
use core::fmt::Display;
use libtechmino_vlq::VlqDecodeError;
use miniz_oxide::{MZError, deflate::core::TDEFLStatus, inflate::TINFLStatus};
use thiserror::Error;
#[derive(Debug, Error)]
pub struct OwnedTypeError {
pub(crate) key: &'static str,
pub(crate) exp_ty: ValueVariant,
pub(crate) value: serde_json::Value,
}
impl OwnedTypeError {
#[must_use]
pub fn inner(&self) -> &serde_json::Value {
&self.value
}
#[must_use]
pub fn take_inner(self) -> serde_json::Value {
self.value
}
#[must_use]
pub fn get_ref(&self) -> TypeError<'_> {
TypeError {
key: self.key,
exp_ty: self.exp_ty,
value: &self.value,
}
}
}
impl Display for OwnedTypeError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
<TypeError as Display>::fmt(&self.get_ref(), f)
}
}
#[derive(Debug, Error)]
pub struct TypeError<'a> {
pub(crate) key: &'static str,
pub(crate) exp_ty: ValueVariant,
pub(crate) value: &'a serde_json::Value,
}
impl<'a> TypeError<'a> {
#[must_use]
pub fn inner(&self) -> &'a serde_json::Value {
self.value
}
}
impl Display for TypeError<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"Entry with key {key} had an unexpected type \
{val_ty} instead of the expected {exp_ty}",
key = self.key,
exp_ty = self.exp_ty.to_str(),
val_ty = ValueVariant::from(self.value).to_str(),
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ValueVariant {
Null,
Bool,
Number,
Float,
Byte,
Long,
String,
#[cfg(feature = "chrono")]
NaiveDateTimeString,
Array,
PieceArray,
PieceColorArray,
Object,
}
impl ValueVariant {
const fn to_str(self) -> &'static str {
match self {
ValueVariant::Null => "null",
ValueVariant::Bool => "bool",
ValueVariant::Number => "(unspecified kind of) number",
ValueVariant::Float => "64-bit floating-point number",
ValueVariant::Byte => "8-bit unsigned integer",
ValueVariant::Long => "64-bit unsigned integer",
ValueVariant::String => "string",
#[cfg(feature = "chrono")]
ValueVariant::NaiveDateTimeString => {
use crate::consts::METADATA_DATE_FORMAT;
const_format::formatc!("datetime string with format '{METADATA_DATE_FORMAT}'")
}
ValueVariant::Array => "array",
ValueVariant::PieceArray => {
const_format::formatc!("array with {TOTAL_PIECE_COUNT} elements")
}
ValueVariant::PieceColorArray => {
const_format::formatc!(
"array of valid color indices with {TOTAL_PIECE_COUNT} elements"
)
}
ValueVariant::Object => "object",
}
}
}
impl From<&serde_json::Value> for ValueVariant {
fn from(value: &serde_json::Value) -> Self {
match value {
serde_json::Value::Null => Self::Null,
serde_json::Value::Bool(_) => Self::Bool,
serde_json::Value::Number(_) => Self::Number,
serde_json::Value::String(_) => Self::String,
serde_json::Value::Array(v) if v.len() == TOTAL_PIECE_COUNT => Self::PieceArray,
serde_json::Value::Array(_) => Self::Array,
serde_json::Value::Object(_) => Self::Object,
}
}
}
#[derive(Debug, Error)]
#[error(
"Failed to create GameInputEvent: Frame number {frame} is greater than max of {max_frame}",
max_frame = GameInputEvent::MAX_FRAME
)]
pub struct GameInputEventError {
pub(crate) frame: u64,
}
#[derive(Debug, Error)]
#[error("Expected value of either 0 or 1, found {value}")]
pub struct NotABool {
pub(crate) value: u8,
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ReplayParseError {
#[error("zlib failed to decompress the replay data")]
ZlibDecompressError {
status: TINFLStatus,
mz_error: MZError,
},
#[error("the given base64 string was not valid base64")]
Base64DecodeError(DecodeError),
#[error("failed to find separator between replay metadata and input data")]
MetadataSeparatorNotFound,
#[error("metadata is not valid utf-8")]
MetadataNotUtf8(#[from] FromUtf8Error),
#[error("failed to deserialize metadata")]
MetadataDeserializeError(#[from] serde_json::Error),
#[error("could not infer input parse mode from version metadata")]
UnknownInputParseMode(Option<Result<String, serde_json::Value>>),
#[error("input data contains invalid vlq")]
MalformedVlqData {
#[from]
inner: VlqDecodeError,
},
#[error("malformed input data")]
MalformedInputData {
raw_frame: u64,
frame: u64,
action: u64,
},
#[error("replay data unexpectedly ended")]
UnexpectedEnd,
}
impl From<DecodeError> for ReplayParseError {
fn from(value: DecodeError) -> Self {
Self::Base64DecodeError(value)
}
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ReplaySerializeError {
#[error("could not infer input parse mode from version metadata")]
UnknownInputParseMode(Option<Result<String, serde_json::Value>>),
#[error(
"unsorted input data: found input for frame {unsorted_time} after input for frame {prev_time}"
)]
UnsortedInput {
prev_time: u64,
unsorted_time: u64,
},
#[error("failed to serialize metadata as JSON")]
MetadataSerializeError(serde_json::Error),
#[error("could not fit {number} into the VLQ format")]
VlqOverflow {
number: u64,
},
#[error("compression error")]
ZlibError {
tdefl_status: TDEFLStatus,
},
}
impl From<serde_json::Error> for ReplaySerializeError {
fn from(value: serde_json::Error) -> Self {
Self::MetadataSerializeError(value)
}
}
impl From<TDEFLStatus> for ReplaySerializeError {
fn from(value: TDEFLStatus) -> Self {
Self::ZlibError {
tdefl_status: value,
}
}
}
#[derive(Debug, thiserror::Error)]
#[error("Unknown first byte in replay stream: {first_byte}")]
pub struct UnknownReplayKind {
pub(crate) first_byte: u8,
}