diff --git a/cli/src/error.rs b/cli/src/error.rs index 708b4d4..b1a4869 100644 --- a/cli/src/error.rs +++ b/cli/src/error.rs @@ -3,7 +3,8 @@ use std::{borrow::Cow, num::ParseIntError, ops::Range}; use oxcr_protocol::{ aott::{ self, - text::{self, CharError}, + error::LabelError, + text::{self, CharError, CharLabel}, }, miette::{self, SourceSpan}, ser::any_of, @@ -61,35 +62,21 @@ pub enum ParseError { #[source] error: ParseIntError, }, - #[error("filter failed in {location}, while checking {token}")] - #[diagnostic(code(aott::error::filter_failed))] - FilterFailed { - #[label = "this is the token that didn't pass"] - at: SourceSpan, - location: &'static core::panic::Location<'static>, - token: char, - }, - #[error("expected keyword {keyword}")] - #[diagnostic(code(aott::text::error::expected_keyword))] - ExpectedKeyword { - #[label = "the keyword here is {found}"] - at: SourceSpan, - keyword: String, - found: String, - }, - #[error("expected digit of radix {radix}")] - #[diagnostic(code(aott::text::error::expected_digit))] - ExpectedDigit { + + #[error("{label}; last token was {last_token:?}")] + Text { #[label = "here"] at: SourceSpan, - radix: u32, - found: char, + label: aott::text::CharLabel, + last_token: Option, }, - #[error("expected identifier character (a-zA-Z or _), but found {found}")] - ExpectedIdent { + + #[error("{label}; last token was {last_token:?}")] + Builtin { #[label = "here"] at: SourceSpan, - found: char, + label: aott::error::BuiltinLabel, + last_token: Option, }, } @@ -105,7 +92,7 @@ pub enum Expectation { Digit(u32), } -impl<'a> aott::error::Error<&'a str> for ParseError { +impl<'a> aott::error::FundamentalError<&'a str> for ParseError { fn unexpected_eof( span: Range, expected: Option::Token>>, @@ -134,45 +121,6 @@ impl<'a> aott::error::Error<&'a str> for ParseError { help: None, } } - - fn filter_failed( - span: Range, - location: &'static core::panic::Location<'static>, - token: <&'a str as aott::prelude::InputType>::Token, - ) -> Self { - Self::FilterFailed { - at: span.into(), - location, - token, - } - } } -impl CharError for ParseError { - fn expected_digit(span: Range, radix: u32, got: char) -> Self { - Self::ExpectedDigit { - at: span.into(), - radix, - found: got, - } - } - - fn expected_ident_char(span: Range, got: char) -> Self { - Self::ExpectedIdent { - at: span.into(), - found: got, - } - } - - fn expected_keyword<'a, 'b: 'a>( - span: Range, - keyword: &'b ::Str, - actual: &'a ::Str, - ) -> Self { - Self::ExpectedKeyword { - at: span.into(), - keyword: keyword.to_owned(), - found: actual.to_owned(), - } - } -} +impl<'a> LabelError<&'a str, CharLabel> for ParseError {} diff --git a/protocol/src/model.rs b/protocol/src/model.rs index c85beba..e0cbe2f 100644 --- a/protocol/src/model.rs +++ b/protocol/src/model.rs @@ -3,12 +3,12 @@ pub mod packets; pub mod registry; mod varint; use self::registry::RegistryItem; -use aott::primitive::one_of; +use aott::primitive::{filter, one_of}; use bytes::BufMut; use std::{ops::RangeInclusive, ptr}; pub use varint::*; -use crate::ser::{Deserialize, Serialize}; +use crate::ser::{Deserialize, Serialize, Type}; pub mod item; pub const MAX_PACKET_DATA: usize = 0x1FFFFF; @@ -43,7 +43,7 @@ impl Deserialize for Difficulty { fn deserialize<'a>( input: &mut aott::prelude::Input<&'a [u8], crate::ser::Extra>, ) -> aott::PResult<&'a [u8], Self, crate::ser::Extra> { - let byte = one_of([0x0, 0x1, 0x2, 0x3])(input)?; + let byte = filter(|x| matches!(x, 0..=3), Type::Difficulty)(input)?; Ok(unsafe { *ptr::addr_of!(byte).cast() }) } } diff --git a/protocol/src/model/packets/play.rs b/protocol/src/model/packets/play.rs index b37e3eb..a861a20 100644 --- a/protocol/src/model/packets/play.rs +++ b/protocol/src/model/packets/play.rs @@ -6,7 +6,7 @@ use crate::{ }; use std::ptr; -use aott::primitive::one_of; +use aott::primitive::{filter, one_of}; use bytes::BufMut; use indexmap::IndexMap; @@ -106,7 +106,7 @@ impl Deserialize for GameMode { fn deserialize<'a>( input: &mut aott::prelude::Input<&'a [u8], Extra>, ) -> aott::PResult<&'a [u8], Self, Extra> { - let byte = one_of([0x0, 0x1, 0x2, 0x3])(input)?; + let byte = filter(|x| matches!(x, 0..=3), Type::GameMode)(input)?; Ok(unsafe { *ptr::addr_of!(byte).cast() }) } } @@ -131,7 +131,7 @@ impl Deserialize for PreviousGameMode { input: &mut aott::prelude::Input<&'a [u8], Extra>, ) -> aott::PResult<&'a [u8], Self, Extra> { let byte = aott::bytes::number::big::i8 - .filter(|g| (-1..=3).contains(g)) + .filter(|g| (-1..=3).contains(g), Type::PreviousGameMode) .parse_with(input)?; Ok(unsafe { *ptr::addr_of!(byte).cast() }) } diff --git a/protocol/src/nbt.rs b/protocol/src/nbt.rs index 7b61db7..2a57e28 100644 --- a/protocol/src/nbt.rs +++ b/protocol/src/nbt.rs @@ -17,10 +17,13 @@ use serde::{ }; use crate::ser::*; +use aott::iter::IterParser; #[derive(Clone, Debug, thiserror::Error, miette::Diagnostic)] pub enum Label { - InvalidTagType(u8) + #[error("invalid tag type {_0}, expected a byte in range 0..=12")] + #[diagnostic(code(nbt::error::invalid_tag_type))] + InvalidTagType(u8), } #[derive(Debug, Clone, derive_more::From)] @@ -256,7 +259,8 @@ impl NbtTag { v.ok_or(crate::error::Error::Nbt(NbtError::ExpectedAnythingButEnd)) }) .repeated() - .exactly(length), + .exactly(length) + .collect::>(), tag, )(input)?; @@ -284,7 +288,7 @@ pub enum NbtTagType { #[parser(extras = "Extra<()>")] fn nbt_tag(input: &[u8]) -> NbtTagType { - any.filter(|b| (0u8..=12u8).contains(b), |b| Label::) + any.filter(|b| (0u8..=12u8).contains(b), Label::InvalidTagType) // SAFETY: in filter we filter the tag types to be in bounds .map(|b| unsafe { *(&b as *const u8 as *const NbtTagType) }) .parse_with(input) @@ -328,10 +332,13 @@ impl Deserialize for SmolArray { debug_assert!(length >= 0); let length = length as usize; - T::deserialize - .repeated_custom::() - .exactly(length) - .parse_with(input) + Ok(Self( + T::deserialize + .repeated() + .exactly(length) + .collect() + .parse_with(input)?, + )) } } diff --git a/protocol/src/ser.rs b/protocol/src/ser.rs index d26f1a9..7fb2e4f 100644 --- a/protocol/src/ser.rs +++ b/protocol/src/ser.rs @@ -21,14 +21,14 @@ use crate::model::VarInt; use ::bytes::{BufMut, Bytes, BytesMut}; pub use aott::prelude::parser; pub use aott::prelude::Parser; -use aott::{error::FundamentalError, pfn_type, prelude::*}; +use aott::{error::FundamentalError, iter::IterParser, pfn_type, prelude::*}; use tracing::debug; mod error; mod types; pub use error::*; -pub use types::*; +pub use types::{Label as Type, *}; pub trait Deserialize: Sized { type Context = (); @@ -143,3 +143,18 @@ pub macro impl_ser($(|$context:ty|)? $ty:ty => [$($(|$cx:ty|)?$field:ident),*$(, crate::ser::deserialize!($(|$context|)? $ty => [$($(|$cx|)?$field,)*]); crate::ser::serialize!($ty => [$($field,)*]); } + +#[inline(always)] +pub fn no_context, EV: ParserExtras>( + parser: impl Parser, +) -> impl Fn(&mut Input) -> Result { + move |input| parser.parse_with(&mut input.no_context()) +} + +#[inline(always)] +pub fn with_context, E2: ParserExtras, C>( + parser: impl Parser, + context: C, +) -> impl Fn(&mut Input) -> Result { + move |input| parser.parse_with(&mut input.with_context(&context)) +} diff --git a/protocol/src/ser/error.rs b/protocol/src/ser/error.rs index ab9e15b..42913ea 100644 --- a/protocol/src/ser/error.rs +++ b/protocol/src/ser/error.rs @@ -35,7 +35,7 @@ pub enum SerializationError { at: SourceSpan, }, - #[error("type error {label}, last token is {last_token:?}")] + #[error("{label}; last token is {last_token:?}")] Type { #[label = "here"] at: SourceSpan, @@ -46,36 +46,75 @@ pub enum SerializationError { last_token: Option, }, - #[error("strign error {label}, last token is {last_token:?}")] + #[error("{label}; last token is {last_token:?}")] String { #[label = "here"] at: SourceSpan, - #[source] label: aott::text::CharLabel, last_token: Option, }, + + #[error("{label}; last token was {last_token:?}")] + Nbt { + #[label = "here"] + at: SourceSpan, + #[diagnostic_source] + #[source] + #[diagnostic(transparent)] + label: crate::nbt::Label, + last_token: Option, + }, + + #[error("{label}; last token was {last_token:?}")] + Builtin { + #[label = "here"] + at: SourceSpan, + label: aott::error::BuiltinLabel, + last_token: Option, + }, } -macro label_error($laty:ty => $variant:ident) { - impl> LabelError - for SerializationError - { - fn from_label(span: Range, label: $laty, last_token: Option) -> Self { - Self::$variant { - at: span.into(), - label, - last_token, +macro_rules! label_error { + ($variant:ident; u8 => $u8:ty) => { + impl<'a> aott::error::LabelError<&'a [u8], $u8> for crate::error::Error { + fn from_label(span: Range, label: $u8, last_token: Option) -> Self { + Self::Ser(SerializationError::$variant { + at: span.into(), + label, + last_token, + }) + } + } + }; + ($variant:ident; char => $char:ty) => { + impl<'a> aott::error::LabelError<&'a str, $char> for crate::error::Error { + fn from_label(span: Range, label: $char, last_token: Option) -> Self { + Self::SerStr(SerializationError::$variant { + at: span.into(), + label, + last_token, + }) } } + }; + ($variant:ident => $u8:ty; $char:ty) => { + label_error!($variant; u8 => $u8); + label_error!($variant; char => $char); + }; + ($laty:ty => $variant:ident) => { + label_error!($variant; u8 => $laty); + label_error!($variant; char => $laty); } } label_error!(super::types::Label => Type); -label_error!(CharLabel => String); +label_error!(String => CharLabel; CharLabel); +label_error!(Nbt; u8 => crate::nbt::Label); +label_error!(aott::error::BuiltinLabel => Builtin); #[derive(thiserror::Error, miette::Diagnostic, Debug)] -#[error("{}", any_of_display(.errors))] -pub struct WithSource { +#[error("{errors:#?}")] +pub struct WithSource { #[source_code] pub src: BytesSource, #[related] diff --git a/protocol/src/ser/types.rs b/protocol/src/ser/types.rs index 26f6000..965c5c9 100644 --- a/protocol/src/ser/types.rs +++ b/protocol/src/ser/types.rs @@ -1,19 +1,25 @@ -use std::cmp::Ordering; - use super::*; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, thiserror::Error, miette::Diagnostic)] pub enum Label { + #[error("expected boolean")] Boolean, + #[error("{_0}")] String(StringError), + #[error("expected previous gamemode (-1 ..= 3), but got {_0}")] + PreviousGameMode(i8), + #[error("expected difficulty (0 ..= 3)")] + Difficulty, + #[error("expected gamemode (0 ..= 3)")] + GameMode, } #[derive(Clone, Debug, thiserror::Error, miette::Diagnostic)] pub enum StringError { - #[error("Length of string (VarInt) was out of bounds for usize: {_0}")] + #[error("length of string (VarInt) was out of bounds for usize: {_0}")] #[diagnostic(code(protocol::ser::types::string::usize_oob))] LengthOOB(i32), - #[error("Length of FixedStr exceeded maximum ({expected}): {actual}")] + #[error("length of FixedStr exceeded maximum ({expected}): {actual}")] #[diagnostic(code(protocol::ser::types::fixed_str::length_bigger_than_expected))] FixedLengthBigger { expected: usize, actual: usize }, } @@ -188,7 +194,7 @@ impl Deserialize for Option { #[parser(extras = "Extra")] fn deserialize(input: &[u8]) -> Self { - let exists = filter(|x| x < 0x2, Label::Boolean) + let exists = filter(|x| *x < 0x2, Label::Boolean) .map(|t| t == 0x1) .parse_with(input)?; @@ -484,9 +490,8 @@ impl Serialize for bool { impl Deserialize for bool { #[parser(extras = "Extra")] - #[track_caller] fn deserialize(input: &[u8]) -> Self { - Ok(one_of([0x0, 0x1])(input)? == 0x1) + Ok(filter(|x| *x < 2, Label::Boolean).parse_with(input)? == 0x1) } }