use crate::{
ContextualCodec, TypeCodec,
basic::{Boolean, Identifier},
};
use mcproto_codec::error::{
CodecError, CodecKind, CodecOperation, ContextRequirement, InvalidEncodingReason,
};
use mcproto_codec::io::{read_exact_counted, write_all_counted};
use mcproto_codec::varint::{VarIntRead, VarIntWrite};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Context {
presence: Option<bool>,
array_length: Option<usize>,
element_contexts: Option<Box<[Context]>>,
}
impl Context {
pub const PRESENT: Self = Self {
presence: Some(true),
array_length: None,
element_contexts: None,
};
pub const ABSENT: Self = Self {
presence: Some(false),
array_length: None,
element_contexts: None,
};
#[must_use]
pub const fn new(present: bool) -> Self {
Self {
presence: Some(present),
array_length: None,
element_contexts: None,
}
}
#[must_use]
pub const fn present() -> Self {
Self::PRESENT
}
#[must_use]
pub const fn absent() -> Self {
Self::ABSENT
}
#[must_use]
pub const fn for_array_length(length: usize) -> Self {
Self {
presence: None,
array_length: Some(length),
element_contexts: None,
}
}
#[must_use]
pub fn with_array_length(self, length: usize) -> Self {
Self {
presence: self.presence,
array_length: Some(length),
element_contexts: self.element_contexts,
}
}
#[must_use]
pub fn with_element_contexts(self, contexts: impl IntoIterator<Item = Context>) -> Self {
Self {
presence: self.presence,
array_length: self.array_length,
element_contexts: Some(contexts.into_iter().collect()),
}
}
#[must_use]
pub const fn presence(&self) -> Option<bool> {
self.presence
}
#[must_use]
pub const fn array_length(&self) -> Option<usize> {
self.array_length
}
fn element_context(
&self,
index: usize,
operation: CodecOperation,
) -> Result<&Context, CodecError> {
match &self.element_contexts {
Some(contexts) => contexts.get(index).ok_or_else(|| {
missing_context(
CodecKind::Array,
operation,
ContextRequirement::ElementContext,
)
}),
None => Ok(self),
}
}
#[must_use]
pub const fn is_present(&self) -> bool {
matches!(self.presence, Some(true))
}
}
fn missing_context(
codec: CodecKind,
operation: CodecOperation,
required: ContextRequirement,
) -> CodecError {
CodecError::invalid_encoding_for_operation(
codec,
operation,
0,
InvalidEncodingReason::MissingContext { required },
)
}
#[repr(transparent)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct Array<T>(
pub Vec<T>,
);
impl<T> Array<T> {
#[must_use]
pub const fn new(values: Vec<T>) -> Self {
Self(values)
}
#[must_use]
pub const fn len(&self) -> usize {
self.0.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[must_use]
pub const fn as_slice(&self) -> &[T] {
self.0.as_slice()
}
#[must_use]
pub fn into_vec(self) -> Vec<T> {
self.0
}
}
impl<T> From<Vec<T>> for Array<T> {
fn from(values: Vec<T>) -> Self {
Self(values)
}
}
impl<T> From<Array<T>> for Vec<T> {
fn from(values: Array<T>) -> Self {
values.0
}
}
impl<T> ContextualCodec for Array<T>
where
T: ContextualCodec,
{
fn encode_with_context(
&self,
writer: &mut impl std::io::Write,
context: &Context,
) -> Result<(), CodecError> {
let expected = context.array_length().ok_or_else(|| {
missing_context(
CodecKind::Array,
CodecOperation::Write,
ContextRequirement::Length,
)
})?;
if self.len() != expected {
return Err(CodecError::invalid_encoding_for_operation(
CodecKind::Array,
CodecOperation::Write,
0,
InvalidEncodingReason::ArrayLengthMismatch {
expected,
actual: self.len(),
},
));
}
for (index, value) in self.0.iter().enumerate() {
let element_context = context.element_context(index, CodecOperation::Write)?;
value
.encode_with_context(writer, element_context)
.map_err(|error| error.with_context(CodecKind::Array))?;
}
Ok(())
}
fn decode_with_context(
reader: &mut impl std::io::Read,
context: &Context,
) -> Result<Self, CodecError> {
let length = context.array_length().ok_or_else(|| {
missing_context(
CodecKind::Array,
CodecOperation::Read,
ContextRequirement::Length,
)
})?;
let mut values = Vec::with_capacity(length);
for index in 0..length {
let element_context = context.element_context(index, CodecOperation::Read)?;
values.push(
T::decode_with_context(reader, element_context)
.map_err(|error| error.with_context(CodecKind::Array))?,
);
}
Ok(Self(values))
}
}
#[repr(transparent)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct ByteArray(
pub Vec<u8>,
);
impl ByteArray {
#[must_use]
pub const fn new(bytes: Vec<u8>) -> Self {
Self(bytes)
}
#[must_use]
pub const fn len(&self) -> usize {
self.0.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[must_use]
pub const fn as_slice(&self) -> &[u8] {
self.0.as_slice()
}
#[must_use]
pub fn into_vec(self) -> Vec<u8> {
self.0
}
}
impl From<Vec<u8>> for ByteArray {
fn from(bytes: Vec<u8>) -> Self {
Self(bytes)
}
}
impl From<ByteArray> for Vec<u8> {
fn from(bytes: ByteArray) -> Self {
bytes.0
}
}
impl ContextualCodec for ByteArray {
fn encode_with_context(
&self,
writer: &mut impl std::io::Write,
context: &Context,
) -> Result<(), CodecError> {
let expected = context.array_length().ok_or_else(|| {
missing_context(
CodecKind::ByteArray,
CodecOperation::Write,
ContextRequirement::Length,
)
})?;
if self.len() != expected {
return Err(CodecError::invalid_encoding_for_operation(
CodecKind::ByteArray,
CodecOperation::Write,
0,
InvalidEncodingReason::ArrayLengthMismatch {
expected,
actual: self.len(),
},
));
}
write_all_counted(writer, &self.0, CodecKind::ByteArray, 0)
}
fn decode_with_context(
reader: &mut impl std::io::Read,
context: &Context,
) -> Result<Self, CodecError> {
let length = context.array_length().ok_or_else(|| {
missing_context(
CodecKind::ByteArray,
CodecOperation::Read,
ContextRequirement::Length,
)
})?;
let mut bytes = vec![0; length];
read_exact_counted(reader, &mut bytes, CodecKind::ByteArray, 0)?;
Ok(Self(bytes))
}
}
#[repr(transparent)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct PrefixedArray<T>(
pub Vec<T>,
);
impl<T> PrefixedArray<T> {
#[must_use]
pub const fn new(values: Vec<T>) -> Self {
Self(values)
}
#[must_use]
pub const fn len(&self) -> usize {
self.0.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[must_use]
pub const fn as_slice(&self) -> &[T] {
self.0.as_slice()
}
#[must_use]
pub fn into_vec(self) -> Vec<T> {
self.0
}
}
impl<T> From<Vec<T>> for PrefixedArray<T> {
fn from(values: Vec<T>) -> Self {
Self(values)
}
}
impl<T> From<PrefixedArray<T>> for Vec<T> {
fn from(values: PrefixedArray<T>) -> Self {
values.0
}
}
impl<T> TypeCodec for PrefixedArray<T>
where
T: TypeCodec,
{
fn encode(&self, writer: &mut impl std::io::Write) -> Result<(), CodecError> {
let length = i32::try_from(self.len()).map_err(|_| {
CodecError::invalid_encoding_for_operation(
CodecKind::PrefixedArray,
CodecOperation::Write,
0,
InvalidEncodingReason::LengthOutOfRange {
max: i32::MAX as usize,
actual: self.len(),
},
)
})?;
writer
.write_varint(length)
.map_err(|error| error.with_context(CodecKind::PrefixedArray))?;
for value in &self.0 {
value
.encode(writer)
.map_err(|error| error.with_context(CodecKind::PrefixedArray))?;
}
Ok(())
}
fn decode(reader: &mut impl std::io::Read) -> Result<Self, CodecError> {
let (length, prefix_size) = reader
.read_varint_with_size()
.map_err(|error| error.with_context(CodecKind::PrefixedArray))?;
if length < 0 {
return Err(CodecError::invalid_encoding(
CodecKind::PrefixedArray,
prefix_size,
InvalidEncodingReason::NegativeLength { value: length },
));
}
let mut values = Vec::new();
for _ in 0..length as usize {
values.push(
T::decode(reader).map_err(|error| error.with_context(CodecKind::PrefixedArray))?,
);
}
Ok(Self(values))
}
}
#[repr(transparent)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct Optional<T>(
pub Option<T>,
);
impl<T> Optional<T> {
#[must_use]
pub const fn some(value: T) -> Self {
Self(Some(value))
}
#[must_use]
pub const fn none() -> Self {
Self(None)
}
#[must_use]
pub const fn is_some(&self) -> bool {
self.0.is_some()
}
#[must_use]
pub const fn is_none(&self) -> bool {
self.0.is_none()
}
#[must_use]
pub const fn as_ref(&self) -> Optional<&T> {
Optional(self.0.as_ref())
}
#[must_use]
pub fn into_option(self) -> Option<T> {
self.0
}
}
impl<T> From<Option<T>> for Optional<T> {
fn from(value: Option<T>) -> Self {
Self(value)
}
}
impl<T> From<Optional<T>> for Option<T> {
fn from(value: Optional<T>) -> Self {
value.0
}
}
impl<T> ContextualCodec for Optional<T>
where
T: ContextualCodec,
{
fn encode_with_context(
&self,
writer: &mut impl std::io::Write,
context: &Context,
) -> Result<(), CodecError> {
let context_present = context.presence().ok_or_else(|| {
missing_context(
CodecKind::Optional,
CodecOperation::Write,
ContextRequirement::Presence,
)
})?;
match (context_present, self.0.as_ref()) {
(true, Some(value)) => value
.encode_with_context(writer, context)
.map_err(|error| error.with_context(CodecKind::Optional)),
(false, None) => Ok(()),
(context_present, value) => Err(CodecError::invalid_encoding_for_operation(
CodecKind::Optional,
CodecOperation::Write,
0,
InvalidEncodingReason::OptionalValueMismatch {
context_present,
value_present: value.is_some(),
},
)),
}
}
fn decode_with_context(
reader: &mut impl std::io::Read,
context: &Context,
) -> Result<Self, CodecError> {
match context.presence().ok_or_else(|| {
missing_context(
CodecKind::Optional,
CodecOperation::Read,
ContextRequirement::Presence,
)
})? {
true => T::decode_with_context(reader, context)
.map(Self::some)
.map_err(|error| error.with_context(CodecKind::Optional)),
false => Ok(Self::none()),
}
}
}
#[repr(transparent)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct PrefixedOptional<T>(
pub Optional<T>,
);
impl<T> PrefixedOptional<T> {
#[must_use]
pub const fn some(value: T) -> Self {
Self(Optional::some(value))
}
#[must_use]
pub const fn none() -> Self {
Self(Optional::none())
}
#[must_use]
pub const fn is_some(&self) -> bool {
self.0.is_some()
}
#[must_use]
pub const fn is_none(&self) -> bool {
self.0.is_none()
}
#[must_use]
pub const fn as_ref(&self) -> PrefixedOptional<&T> {
PrefixedOptional(self.0.as_ref())
}
#[must_use]
pub fn into_option(self) -> Option<T> {
self.0.into_option()
}
}
impl<T> From<Option<T>> for PrefixedOptional<T> {
fn from(value: Option<T>) -> Self {
Self(value.into())
}
}
impl<T> From<Optional<T>> for PrefixedOptional<T> {
fn from(value: Optional<T>) -> Self {
Self(value)
}
}
impl<T> From<PrefixedOptional<T>> for Option<T> {
fn from(value: PrefixedOptional<T>) -> Self {
value.into_option()
}
}
impl<T> TypeCodec for PrefixedOptional<T>
where
T: TypeCodec,
{
fn encode(&self, writer: &mut impl std::io::Write) -> Result<(), CodecError> {
let context = Context::new(self.is_some());
Boolean(self.is_some())
.encode(writer)
.map_err(|error| error.with_context(CodecKind::PrefixedOptional))?;
self.0
.encode_with_context(writer, &context)
.map_err(|error| error.with_context(CodecKind::PrefixedOptional))
}
fn decode(reader: &mut impl std::io::Read) -> Result<Self, CodecError> {
let present = Boolean::decode(reader)
.map_err(|error| error.with_context(CodecKind::PrefixedOptional))?;
Optional::decode_with_context(reader, &Context::new(present.0))
.map(Self)
.map_err(|error| error.with_context(CodecKind::PrefixedOptional))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Either<X, Y> {
X(X),
Y(Y),
}
impl<X, Y> Either<X, Y> {
#[must_use]
pub const fn is_x(&self) -> bool {
matches!(self, Self::X(_))
}
#[must_use]
pub const fn is_y(&self) -> bool {
matches!(self, Self::Y(_))
}
#[must_use]
pub const fn as_ref(&self) -> Either<&X, &Y> {
match self {
Self::X(value) => Either::X(value),
Self::Y(value) => Either::Y(value),
}
}
#[must_use]
pub fn into_x(self) -> Option<X> {
match self {
Self::X(value) => Some(value),
Self::Y(_) => None,
}
}
#[must_use]
pub fn into_y(self) -> Option<Y> {
match self {
Self::X(_) => None,
Self::Y(value) => Some(value),
}
}
}
impl<X, Y> TypeCodec for Either<X, Y>
where
X: TypeCodec,
Y: TypeCodec,
{
fn encode(&self, writer: &mut impl std::io::Write) -> Result<(), CodecError> {
match self {
Self::X(value) => {
Boolean(true)
.encode(writer)
.map_err(|error| error.with_context(CodecKind::Either))?;
value
.encode(writer)
.map_err(|error| error.with_context(CodecKind::Either))
}
Self::Y(value) => {
Boolean(false)
.encode(writer)
.map_err(|error| error.with_context(CodecKind::Either))?;
value
.encode(writer)
.map_err(|error| error.with_context(CodecKind::Either))
}
}
}
fn decode(reader: &mut impl std::io::Read) -> Result<Self, CodecError> {
if Boolean::decode(reader)
.map_err(|error| error.with_context(CodecKind::Either))?
.0
{
X::decode(reader)
.map(Self::X)
.map_err(|error| error.with_context(CodecKind::Either))
} else {
Y::decode(reader)
.map(Self::Y)
.map_err(|error| error.with_context(CodecKind::Either))
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum IdOr<T> {
Id(i32),
Inline(T),
}
impl<T> IdOr<T> {
#[must_use]
pub const fn id(id: i32) -> Self {
Self::Id(id)
}
#[must_use]
pub const fn inline(value: T) -> Self {
Self::Inline(value)
}
#[must_use]
pub const fn is_id(&self) -> bool {
matches!(self, Self::Id(_))
}
#[must_use]
pub const fn is_inline(&self) -> bool {
matches!(self, Self::Inline(_))
}
#[must_use]
pub const fn registry_id(&self) -> Option<i32> {
match self {
Self::Id(id) => Some(*id),
Self::Inline(_) => None,
}
}
#[must_use]
pub const fn inline_value(&self) -> Option<&T> {
match self {
Self::Id(_) => None,
Self::Inline(value) => Some(value),
}
}
#[must_use]
pub const fn as_ref(&self) -> IdOr<&T> {
match self {
Self::Id(id) => IdOr::Id(*id),
Self::Inline(value) => IdOr::Inline(value),
}
}
#[must_use]
pub fn into_inline(self) -> Option<T> {
match self {
Self::Id(_) => None,
Self::Inline(value) => Some(value),
}
}
}
impl<T> From<T> for IdOr<T> {
fn from(value: T) -> Self {
Self::Inline(value)
}
}
impl<T> TypeCodec for IdOr<T>
where
T: TypeCodec,
{
fn encode(&self, writer: &mut impl std::io::Write) -> Result<(), CodecError> {
match self {
Self::Id(id) => {
let selector = id.checked_add(1).filter(|_| *id >= 0).ok_or_else(|| {
CodecError::invalid_encoding_for_operation(
CodecKind::IdOr,
CodecOperation::Write,
0,
InvalidEncodingReason::InvalidRegistryId {
value: *id,
max: i32::MAX - 1,
},
)
})?;
writer
.write_varint(selector)
.map_err(|error| error.with_context(CodecKind::IdOr))
}
Self::Inline(value) => {
writer
.write_varint(0)
.map_err(|error| error.with_context(CodecKind::IdOr))?;
value
.encode(writer)
.map_err(|error| error.with_context(CodecKind::IdOr))
}
}
}
fn decode(reader: &mut impl std::io::Read) -> Result<Self, CodecError> {
let (selector, prefix_size) = reader
.read_varint_with_size()
.map_err(|error| error.with_context(CodecKind::IdOr))?;
match selector {
0 => T::decode(reader)
.map(Self::Inline)
.map_err(|error| error.with_context(CodecKind::IdOr)),
1.. => Ok(Self::Id(selector - 1)),
_ => Err(CodecError::invalid_encoding(
CodecKind::IdOr,
prefix_size,
InvalidEncodingReason::InvalidIdOrSelector { value: selector },
)),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum IdSet {
Tag(Identifier),
Inline(Vec<i32>),
}
impl IdSet {
#[must_use]
pub const fn tag(tag_name: Identifier) -> Self {
Self::Tag(tag_name)
}
#[must_use]
pub const fn inline(ids: Vec<i32>) -> Self {
Self::Inline(ids)
}
#[must_use]
pub const fn is_tag(&self) -> bool {
matches!(self, Self::Tag(_))
}
#[must_use]
pub const fn is_inline(&self) -> bool {
matches!(self, Self::Inline(_))
}
#[must_use]
pub const fn tag_name(&self) -> Option<&Identifier> {
match self {
Self::Tag(tag_name) => Some(tag_name),
Self::Inline(_) => None,
}
}
#[must_use]
pub const fn ids(&self) -> Option<&[i32]> {
match self {
Self::Tag(_) => None,
Self::Inline(ids) => Some(ids.as_slice()),
}
}
#[must_use]
pub fn into_tag(self) -> Option<Identifier> {
match self {
Self::Tag(tag_name) => Some(tag_name),
Self::Inline(_) => None,
}
}
#[must_use]
pub fn into_ids(self) -> Option<Vec<i32>> {
match self {
Self::Tag(_) => None,
Self::Inline(ids) => Some(ids),
}
}
}
impl From<Identifier> for IdSet {
fn from(tag_name: Identifier) -> Self {
Self::Tag(tag_name)
}
}
impl From<Vec<i32>> for IdSet {
fn from(ids: Vec<i32>) -> Self {
Self::Inline(ids)
}
}
impl TypeCodec for IdSet {
fn encode(&self, writer: &mut impl std::io::Write) -> Result<(), CodecError> {
match self {
Self::Tag(tag_name) => {
writer
.write_varint(0)
.map_err(|error| error.with_context(CodecKind::IdSet))?;
tag_name
.encode(writer)
.map_err(|error| error.with_context(CodecKind::IdSet))
}
Self::Inline(ids) => {
if let Some(id) = ids.iter().copied().find(|id| *id < 0) {
return Err(CodecError::invalid_encoding_for_operation(
CodecKind::IdSet,
CodecOperation::Write,
0,
InvalidEncodingReason::InvalidRegistryId {
value: id,
max: i32::MAX,
},
));
}
let type_value = i32::try_from(ids.len())
.ok()
.and_then(|length| length.checked_add(1))
.ok_or_else(|| {
CodecError::invalid_encoding_for_operation(
CodecKind::IdSet,
CodecOperation::Write,
0,
InvalidEncodingReason::LengthOutOfRange {
max: (i32::MAX - 1) as usize,
actual: ids.len(),
},
)
})?;
writer
.write_varint(type_value)
.map_err(|error| error.with_context(CodecKind::IdSet))?;
for id in ids {
writer
.write_varint(*id)
.map_err(|error| error.with_context(CodecKind::IdSet))?;
}
Ok(())
}
}
}
fn decode(reader: &mut impl std::io::Read) -> Result<Self, CodecError> {
let (type_value, type_size) = reader
.read_varint_with_size()
.map_err(|error| error.with_context(CodecKind::IdSet))?;
match type_value {
0 => Identifier::decode(reader)
.map(Self::Tag)
.map_err(|error| error.with_context(CodecKind::IdSet)),
1.. => {
let length = (type_value - 1) as usize;
let mut bytes_processed = type_size;
let mut ids = Vec::new();
for _ in 0..length {
let (id, id_size) = reader
.read_varint_with_size()
.map_err(|error| error.with_context(CodecKind::IdSet))?;
bytes_processed += id_size;
if id < 0 {
return Err(CodecError::invalid_encoding(
CodecKind::IdSet,
bytes_processed,
InvalidEncodingReason::InvalidRegistryId {
value: id,
max: i32::MAX,
},
));
}
ids.push(id);
}
Ok(Self::Inline(ids))
}
_ => Err(CodecError::invalid_encoding(
CodecKind::IdSet,
type_size,
InvalidEncodingReason::InvalidIdSetType { value: type_value },
)),
}
}
}