mcproto_types/contextual.rs
1//! Context and protocol values whose wire representation depends on their
2//! enclosing packet or data structure.
3
4use crate::{
5 ContextualCodec, TypeCodec,
6 basic::{Boolean, Identifier},
7};
8use mcproto_codec::error::{
9 CodecError, CodecKind, CodecOperation, ContextRequirement, InvalidEncodingReason,
10};
11use mcproto_codec::io::{read_exact_counted, write_all_counted};
12use mcproto_codec::varint::{VarIntRead, VarIntWrite};
13
14/// External information required to encode or decode a contextual value.
15///
16/// The context can record whether the current field is present, the length of
17/// an array field, and child contexts for array elements. This supports
18/// protocol fields described as `Optional X` or `Array of X`, where the
19/// required information is known from the enclosing packet or data structure.
20///
21/// A `Context` does not consume or produce any bytes. The enclosing codec must
22/// derive it from already-known protocol state and pass it to
23/// [`ContextualCodec`](crate::ContextualCodec).
24///
25/// # Examples
26///
27/// ```
28/// use mcproto_types::contextual::Context;
29///
30/// let has_signature = true; // Derived from an earlier packet field.
31/// let context = Context::new(has_signature);
32/// assert!(context.is_present());
33/// assert!(!Context::absent().is_present());
34/// ```
35#[derive(Debug, Clone, PartialEq, Eq, Hash)]
36pub struct Context {
37 presence: Option<bool>,
38 array_length: Option<usize>,
39 element_contexts: Option<Box<[Context]>>,
40}
41
42impl Context {
43 /// Context for a field that is present on the wire.
44 pub const PRESENT: Self = Self {
45 presence: Some(true),
46 array_length: None,
47 element_contexts: None,
48 };
49
50 /// Context for a field that occupies zero bytes on the wire.
51 pub const ABSENT: Self = Self {
52 presence: Some(false),
53 array_length: None,
54 element_contexts: None,
55 };
56
57 /// Creates a context from a presence condition determined by the enclosing
58 /// protocol structure.
59 #[must_use]
60 pub const fn new(present: bool) -> Self {
61 Self {
62 presence: Some(present),
63 array_length: None,
64 element_contexts: None,
65 }
66 }
67
68 /// Creates context for a field that is present on the wire.
69 #[must_use]
70 pub const fn present() -> Self {
71 Self::PRESENT
72 }
73
74 /// Creates context for a field that occupies zero bytes on the wire.
75 #[must_use]
76 pub const fn absent() -> Self {
77 Self::ABSENT
78 }
79
80 /// Creates context containing the number of elements in an array field.
81 #[must_use]
82 pub const fn for_array_length(length: usize) -> Self {
83 Self {
84 presence: None,
85 array_length: Some(length),
86 element_contexts: None,
87 }
88 }
89
90 /// Adds an array length to this context, preserving any presence state.
91 #[must_use]
92 pub fn with_array_length(self, length: usize) -> Self {
93 Self {
94 presence: self.presence,
95 array_length: Some(length),
96 element_contexts: self.element_contexts,
97 }
98 }
99
100 /// Adds a child context for each array element.
101 ///
102 /// When child contexts are not supplied, an array passes its own context
103 /// to every element. Supplying child contexts is required when different
104 /// elements have different contextual metadata or when arrays are nested.
105 #[must_use]
106 pub fn with_element_contexts(self, contexts: impl IntoIterator<Item = Context>) -> Self {
107 Self {
108 presence: self.presence,
109 array_length: self.array_length,
110 element_contexts: Some(contexts.into_iter().collect()),
111 }
112 }
113
114 /// Returns the explicitly supplied presence state, if one exists.
115 #[must_use]
116 pub const fn presence(&self) -> Option<bool> {
117 self.presence
118 }
119
120 /// Returns the contextual array length, if one exists.
121 #[must_use]
122 pub const fn array_length(&self) -> Option<usize> {
123 self.array_length
124 }
125
126 fn element_context(
127 &self,
128 index: usize,
129 operation: CodecOperation,
130 ) -> Result<&Context, CodecError> {
131 match &self.element_contexts {
132 Some(contexts) => contexts.get(index).ok_or_else(|| {
133 missing_context(
134 CodecKind::Array,
135 operation,
136 ContextRequirement::ElementContext,
137 )
138 }),
139 None => Ok(self),
140 }
141 }
142
143 /// Returns whether the contextual field is present on the wire.
144 #[must_use]
145 pub const fn is_present(&self) -> bool {
146 matches!(self.presence, Some(true))
147 }
148}
149
150fn missing_context(
151 codec: CodecKind,
152 operation: CodecOperation,
153 required: ContextRequirement,
154) -> CodecError {
155 CodecError::invalid_encoding_for_operation(
156 codec,
157 operation,
158 0,
159 InvalidEncodingReason::MissingContext { required },
160 )
161}
162
163/// A sequence of protocol values whose element count is supplied by context.
164///
165/// `Array<T>` has no wire length prefix. The enclosing packet must supply the
166/// number of elements through [`Context::for_array_length`] or
167/// [`Context::with_array_length`]. Exactly that many values are encoded or
168/// decoded. A zero length therefore produces and consumes zero bytes.
169///
170/// The total byte size is not necessarily `length * fixed_size`: if `T` has a
171/// variable-size encoding, each element may occupy a different number of
172/// bytes.
173///
174/// # Examples
175///
176/// ```
177/// use mcproto_types::{ContextualCodec, TypeCodec, basic::UnsignedByte};
178/// use mcproto_types::contextual::{Array, Context};
179///
180/// let values = Array(vec![UnsignedByte(1), UnsignedByte(2)]);
181/// let mut encoded = Vec::new();
182/// values.encode_with_context(&mut encoded, &Context::for_array_length(2))?;
183/// assert_eq!(encoded, [1, 2]);
184///
185/// let mut input = encoded.as_slice();
186/// assert_eq!(
187/// Array::<UnsignedByte>::decode_with_context(
188/// &mut input,
189/// &Context::for_array_length(2),
190/// )?,
191/// values,
192/// );
193/// # Ok::<(), mcproto_codec::error::CodecError>(())
194/// ```
195#[repr(transparent)]
196#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
197pub struct Array<T>(
198 /// The array elements.
199 pub Vec<T>,
200);
201
202impl<T> Array<T> {
203 /// Creates an array from its elements.
204 #[must_use]
205 pub const fn new(values: Vec<T>) -> Self {
206 Self(values)
207 }
208
209 /// Returns the number of elements.
210 #[must_use]
211 pub const fn len(&self) -> usize {
212 self.0.len()
213 }
214
215 /// Returns whether the array contains no elements.
216 #[must_use]
217 pub const fn is_empty(&self) -> bool {
218 self.0.is_empty()
219 }
220
221 /// Returns the elements as a slice.
222 #[must_use]
223 pub const fn as_slice(&self) -> &[T] {
224 self.0.as_slice()
225 }
226
227 /// Extracts the underlying vector.
228 #[must_use]
229 pub fn into_vec(self) -> Vec<T> {
230 self.0
231 }
232}
233
234impl<T> From<Vec<T>> for Array<T> {
235 fn from(values: Vec<T>) -> Self {
236 Self(values)
237 }
238}
239
240impl<T> From<Array<T>> for Vec<T> {
241 fn from(values: Array<T>) -> Self {
242 values.0
243 }
244}
245
246impl<T> ContextualCodec for Array<T>
247where
248 T: ContextualCodec,
249{
250 fn encode_with_context(
251 &self,
252 writer: &mut impl std::io::Write,
253 context: &Context,
254 ) -> Result<(), CodecError> {
255 let expected = context.array_length().ok_or_else(|| {
256 missing_context(
257 CodecKind::Array,
258 CodecOperation::Write,
259 ContextRequirement::Length,
260 )
261 })?;
262 if self.len() != expected {
263 return Err(CodecError::invalid_encoding_for_operation(
264 CodecKind::Array,
265 CodecOperation::Write,
266 0,
267 InvalidEncodingReason::ArrayLengthMismatch {
268 expected,
269 actual: self.len(),
270 },
271 ));
272 }
273
274 for (index, value) in self.0.iter().enumerate() {
275 let element_context = context.element_context(index, CodecOperation::Write)?;
276 value
277 .encode_with_context(writer, element_context)
278 .map_err(|error| error.with_context(CodecKind::Array))?;
279 }
280 Ok(())
281 }
282
283 fn decode_with_context(
284 reader: &mut impl std::io::Read,
285 context: &Context,
286 ) -> Result<Self, CodecError> {
287 let length = context.array_length().ok_or_else(|| {
288 missing_context(
289 CodecKind::Array,
290 CodecOperation::Read,
291 ContextRequirement::Length,
292 )
293 })?;
294 let mut values = Vec::with_capacity(length);
295 for index in 0..length {
296 let element_context = context.element_context(index, CodecOperation::Read)?;
297 values.push(
298 T::decode_with_context(reader, element_context)
299 .map_err(|error| error.with_context(CodecKind::Array))?,
300 );
301 }
302 Ok(Self(values))
303 }
304}
305
306/// A raw sequence of bytes whose length is supplied by context.
307///
308/// A `ByteArray` has no wire length prefix. Its meaning and number of bytes
309/// are determined by the enclosing packet or data structure, which supplies
310/// the length through [`Context::for_array_length`] or
311/// [`Context::with_array_length`]. It is encoded as exactly that many bytes:
312///
313/// ```text
314/// byte[0] + byte[1] + ... + byte[length - 1]
315/// ```
316///
317/// This differs from [`PrefixedArray`], which stores its own VarInt length,
318/// and from [`Array`], which supports arbitrary contextual element codecs.
319/// `ByteArray` writes and reads its byte buffer in one operation and does not
320/// use array element contexts.
321///
322/// [Minecraft protocol Byte Array]: https://minecraft.wiki/w/Java_Edition_protocol/Packets#Byte_Array
323///
324/// # Examples
325///
326/// ```
327/// use mcproto_types::ContextualCodec;
328/// use mcproto_types::contextual::{ByteArray, Context};
329///
330/// let value = ByteArray(vec![0xde, 0xad, 0xbe, 0xef]);
331/// let context = Context::for_array_length(4);
332/// let mut encoded = Vec::new();
333/// value.encode_with_context(&mut encoded, &context)?;
334/// assert_eq!(encoded, [0xde, 0xad, 0xbe, 0xef]);
335///
336/// let mut input = encoded.as_slice();
337/// assert_eq!(ByteArray::decode_with_context(&mut input, &context)?, value);
338/// assert!(input.is_empty());
339/// # Ok::<(), mcproto_codec::error::CodecError>(())
340/// ```
341#[repr(transparent)]
342#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
343pub struct ByteArray(
344 /// The raw bytes.
345 pub Vec<u8>,
346);
347
348impl ByteArray {
349 /// Creates a byte array from raw bytes.
350 #[must_use]
351 pub const fn new(bytes: Vec<u8>) -> Self {
352 Self(bytes)
353 }
354
355 /// Returns the number of bytes.
356 #[must_use]
357 pub const fn len(&self) -> usize {
358 self.0.len()
359 }
360
361 /// Returns whether this byte array is empty.
362 #[must_use]
363 pub const fn is_empty(&self) -> bool {
364 self.0.is_empty()
365 }
366
367 /// Returns the bytes as a slice.
368 #[must_use]
369 pub const fn as_slice(&self) -> &[u8] {
370 self.0.as_slice()
371 }
372
373 /// Extracts the underlying byte vector.
374 #[must_use]
375 pub fn into_vec(self) -> Vec<u8> {
376 self.0
377 }
378}
379
380impl From<Vec<u8>> for ByteArray {
381 fn from(bytes: Vec<u8>) -> Self {
382 Self(bytes)
383 }
384}
385
386impl From<ByteArray> for Vec<u8> {
387 fn from(bytes: ByteArray) -> Self {
388 bytes.0
389 }
390}
391
392impl ContextualCodec for ByteArray {
393 fn encode_with_context(
394 &self,
395 writer: &mut impl std::io::Write,
396 context: &Context,
397 ) -> Result<(), CodecError> {
398 let expected = context.array_length().ok_or_else(|| {
399 missing_context(
400 CodecKind::ByteArray,
401 CodecOperation::Write,
402 ContextRequirement::Length,
403 )
404 })?;
405 if self.len() != expected {
406 return Err(CodecError::invalid_encoding_for_operation(
407 CodecKind::ByteArray,
408 CodecOperation::Write,
409 0,
410 InvalidEncodingReason::ArrayLengthMismatch {
411 expected,
412 actual: self.len(),
413 },
414 ));
415 }
416
417 write_all_counted(writer, &self.0, CodecKind::ByteArray, 0)
418 }
419
420 fn decode_with_context(
421 reader: &mut impl std::io::Read,
422 context: &Context,
423 ) -> Result<Self, CodecError> {
424 let length = context.array_length().ok_or_else(|| {
425 missing_context(
426 CodecKind::ByteArray,
427 CodecOperation::Read,
428 ContextRequirement::Length,
429 )
430 })?;
431 let mut bytes = vec![0; length];
432 read_exact_counted(reader, &mut bytes, CodecKind::ByteArray, 0)?;
433 Ok(Self(bytes))
434 }
435}
436
437/// A sequence prefixed by its element count as a VarInt.
438///
439/// The [Minecraft protocol Prefixed Array] wire representation is a
440/// non-negative [`VarInt`] length followed by exactly that many `T` values:
441///
442/// ```text
443/// VarInt(length) + T[0] + T[1] + ... + T[length - 1]
444/// ```
445///
446/// A zero length is encoded as `0x00` and has no element payload. Since the
447/// prefix is a signed 32-bit VarInt, arrays cannot contain more than
448/// 2,147,483,647 elements. This type supports all context-independent
449/// protocol values through `T: TypeCodec`.
450///
451/// [Minecraft protocol Prefixed Array]: https://minecraft.wiki/w/Java_Edition_protocol/Packets#Prefixed_Array
452/// [`VarInt`]: crate::basic::VarInt
453///
454/// # Examples
455///
456/// ```
457/// use mcproto_types::{TypeCodec, basic::UnsignedByte};
458/// use mcproto_types::contextual::PrefixedArray;
459///
460/// let values = PrefixedArray(vec![UnsignedByte(1), UnsignedByte(2)]);
461/// let mut encoded = Vec::new();
462/// values.encode(&mut encoded)?;
463/// assert_eq!(encoded, [0x02, 0x01, 0x02]);
464///
465/// let mut input = encoded.as_slice();
466/// assert_eq!(PrefixedArray::<UnsignedByte>::decode(&mut input)?, values);
467/// assert!(input.is_empty());
468/// # Ok::<(), mcproto_codec::error::CodecError>(())
469/// ```
470#[repr(transparent)]
471#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
472pub struct PrefixedArray<T>(
473 /// The array elements.
474 pub Vec<T>,
475);
476
477impl<T> PrefixedArray<T> {
478 /// Creates a length-prefixed array from its elements.
479 #[must_use]
480 pub const fn new(values: Vec<T>) -> Self {
481 Self(values)
482 }
483
484 /// Returns the number of elements.
485 #[must_use]
486 pub const fn len(&self) -> usize {
487 self.0.len()
488 }
489
490 /// Returns whether the array contains no elements.
491 #[must_use]
492 pub const fn is_empty(&self) -> bool {
493 self.0.is_empty()
494 }
495
496 /// Returns the elements as a slice.
497 #[must_use]
498 pub const fn as_slice(&self) -> &[T] {
499 self.0.as_slice()
500 }
501
502 /// Extracts the underlying vector.
503 #[must_use]
504 pub fn into_vec(self) -> Vec<T> {
505 self.0
506 }
507}
508
509impl<T> From<Vec<T>> for PrefixedArray<T> {
510 fn from(values: Vec<T>) -> Self {
511 Self(values)
512 }
513}
514
515impl<T> From<PrefixedArray<T>> for Vec<T> {
516 fn from(values: PrefixedArray<T>) -> Self {
517 values.0
518 }
519}
520
521impl<T> TypeCodec for PrefixedArray<T>
522where
523 T: TypeCodec,
524{
525 fn encode(&self, writer: &mut impl std::io::Write) -> Result<(), CodecError> {
526 let length = i32::try_from(self.len()).map_err(|_| {
527 CodecError::invalid_encoding_for_operation(
528 CodecKind::PrefixedArray,
529 CodecOperation::Write,
530 0,
531 InvalidEncodingReason::LengthOutOfRange {
532 max: i32::MAX as usize,
533 actual: self.len(),
534 },
535 )
536 })?;
537
538 writer
539 .write_varint(length)
540 .map_err(|error| error.with_context(CodecKind::PrefixedArray))?;
541 for value in &self.0 {
542 value
543 .encode(writer)
544 .map_err(|error| error.with_context(CodecKind::PrefixedArray))?;
545 }
546 Ok(())
547 }
548
549 fn decode(reader: &mut impl std::io::Read) -> Result<Self, CodecError> {
550 let (length, prefix_size) = reader
551 .read_varint_with_size()
552 .map_err(|error| error.with_context(CodecKind::PrefixedArray))?;
553 if length < 0 {
554 return Err(CodecError::invalid_encoding(
555 CodecKind::PrefixedArray,
556 prefix_size,
557 InvalidEncodingReason::NegativeLength { value: length },
558 ));
559 }
560
561 let mut values = Vec::new();
562 for _ in 0..length as usize {
563 values.push(
564 T::decode(reader).map_err(|error| error.with_context(CodecKind::PrefixedArray))?,
565 );
566 }
567 Ok(Self(values))
568 }
569}
570
571/// A context-controlled optional value of protocol type `T`.
572///
573/// `Optional<T>` stores an [`Option<T>`], but it does not encode a presence
574/// marker. When the supplied [`Context`] is present, the inner `T` is encoded
575/// using its [`ContextualCodec`] implementation. When the context is absent,
576/// the value occupies zero bytes.
577///
578/// The caller must derive the context from the enclosing packet or data
579/// structure. This type therefore implements [`ContextualCodec`], not
580/// [`TypeCodec`]. A value/context mismatch is reported as an encoding error;
581/// it is never silently discarded.
582///
583/// # Examples
584///
585/// ```
586/// use mcproto_types::{ContextualCodec, TypeCodec, basic::UnsignedByte};
587/// use mcproto_types::contextual::{Context, Optional};
588///
589/// let value = Optional::some(UnsignedByte(0xab));
590/// let mut encoded = Vec::new();
591/// value.encode_with_context(&mut encoded, &Context::present())?;
592/// assert_eq!(encoded, [0xab]);
593///
594/// let mut input = encoded.as_slice();
595/// assert_eq!(
596/// Optional::<UnsignedByte>::decode_with_context(&mut input, &Context::present())?,
597/// value,
598/// );
599/// # Ok::<(), mcproto_codec::error::CodecError>(())
600/// ```
601#[repr(transparent)]
602#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
603pub struct Optional<T>(
604 /// The optional value held in memory.
605 pub Option<T>,
606);
607
608impl<T> Optional<T> {
609 /// Creates an optional value that is present.
610 #[must_use]
611 pub const fn some(value: T) -> Self {
612 Self(Some(value))
613 }
614
615 /// Creates an optional value that is absent.
616 #[must_use]
617 pub const fn none() -> Self {
618 Self(None)
619 }
620
621 /// Returns whether this wrapper contains a value.
622 #[must_use]
623 pub const fn is_some(&self) -> bool {
624 self.0.is_some()
625 }
626
627 /// Returns whether this wrapper contains no value.
628 #[must_use]
629 pub const fn is_none(&self) -> bool {
630 self.0.is_none()
631 }
632
633 /// Returns the contained value by reference, if present.
634 #[must_use]
635 pub const fn as_ref(&self) -> Optional<&T> {
636 Optional(self.0.as_ref())
637 }
638
639 /// Extracts the wrapped [`Option<T>`].
640 #[must_use]
641 pub fn into_option(self) -> Option<T> {
642 self.0
643 }
644}
645
646impl<T> From<Option<T>> for Optional<T> {
647 fn from(value: Option<T>) -> Self {
648 Self(value)
649 }
650}
651
652impl<T> From<Optional<T>> for Option<T> {
653 fn from(value: Optional<T>) -> Self {
654 value.0
655 }
656}
657
658impl<T> ContextualCodec for Optional<T>
659where
660 T: ContextualCodec,
661{
662 fn encode_with_context(
663 &self,
664 writer: &mut impl std::io::Write,
665 context: &Context,
666 ) -> Result<(), CodecError> {
667 let context_present = context.presence().ok_or_else(|| {
668 missing_context(
669 CodecKind::Optional,
670 CodecOperation::Write,
671 ContextRequirement::Presence,
672 )
673 })?;
674 match (context_present, self.0.as_ref()) {
675 (true, Some(value)) => value
676 .encode_with_context(writer, context)
677 .map_err(|error| error.with_context(CodecKind::Optional)),
678 (false, None) => Ok(()),
679 (context_present, value) => Err(CodecError::invalid_encoding_for_operation(
680 CodecKind::Optional,
681 CodecOperation::Write,
682 0,
683 InvalidEncodingReason::OptionalValueMismatch {
684 context_present,
685 value_present: value.is_some(),
686 },
687 )),
688 }
689 }
690
691 fn decode_with_context(
692 reader: &mut impl std::io::Read,
693 context: &Context,
694 ) -> Result<Self, CodecError> {
695 match context.presence().ok_or_else(|| {
696 missing_context(
697 CodecKind::Optional,
698 CodecOperation::Read,
699 ContextRequirement::Presence,
700 )
701 })? {
702 true => T::decode_with_context(reader, context)
703 .map(Self::some)
704 .map_err(|error| error.with_context(CodecKind::Optional)),
705 false => Ok(Self::none()),
706 }
707 }
708}
709
710/// An optional value prefixed by a boolean presence marker.
711///
712/// The wire format is a [`Boolean`] followed by `T` when the boolean is true:
713///
714/// ```text
715/// Boolean(is present) + (is present ? T : nothing)
716/// ```
717///
718/// Unlike [`Optional<T>`], this type implements [`TypeCodec`] because its wire
719/// representation contains its own presence marker. The marker is `0x01`
720/// when the wrapped value is [`Some`](Option::Some), and `0x00` when it is
721/// [`None`](Option::None).
722///
723/// # Examples
724///
725/// ```
726/// use mcproto_types::{TypeCodec, basic::UnsignedByte};
727/// use mcproto_types::contextual::PrefixedOptional;
728///
729/// let value = PrefixedOptional::some(UnsignedByte(0xab));
730/// let mut encoded = Vec::new();
731/// value.encode(&mut encoded)?;
732/// assert_eq!(encoded, [0x01, 0xab]);
733///
734/// let mut input = encoded.as_slice();
735/// assert_eq!(PrefixedOptional::<UnsignedByte>::decode(&mut input)?, value);
736/// assert!(input.is_empty());
737/// # Ok::<(), mcproto_codec::error::CodecError>(())
738/// ```
739#[repr(transparent)]
740#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
741pub struct PrefixedOptional<T>(
742 /// The optional value and its context-controlled encoding behavior.
743 pub Optional<T>,
744);
745
746impl<T> PrefixedOptional<T> {
747 /// Creates a prefixed optional containing `value`.
748 #[must_use]
749 pub const fn some(value: T) -> Self {
750 Self(Optional::some(value))
751 }
752
753 /// Creates a prefixed optional with no value.
754 #[must_use]
755 pub const fn none() -> Self {
756 Self(Optional::none())
757 }
758
759 /// Returns whether the prefixed optional contains a value.
760 #[must_use]
761 pub const fn is_some(&self) -> bool {
762 self.0.is_some()
763 }
764
765 /// Returns whether the prefixed optional contains no value.
766 #[must_use]
767 pub const fn is_none(&self) -> bool {
768 self.0.is_none()
769 }
770
771 /// Returns the contained value by reference, if present.
772 #[must_use]
773 pub const fn as_ref(&self) -> PrefixedOptional<&T> {
774 PrefixedOptional(self.0.as_ref())
775 }
776
777 /// Extracts the wrapped [`Option<T>`].
778 #[must_use]
779 pub fn into_option(self) -> Option<T> {
780 self.0.into_option()
781 }
782}
783
784impl<T> From<Option<T>> for PrefixedOptional<T> {
785 fn from(value: Option<T>) -> Self {
786 Self(value.into())
787 }
788}
789
790impl<T> From<Optional<T>> for PrefixedOptional<T> {
791 fn from(value: Optional<T>) -> Self {
792 Self(value)
793 }
794}
795
796impl<T> From<PrefixedOptional<T>> for Option<T> {
797 fn from(value: PrefixedOptional<T>) -> Self {
798 value.into_option()
799 }
800}
801
802impl<T> TypeCodec for PrefixedOptional<T>
803where
804 T: TypeCodec,
805{
806 fn encode(&self, writer: &mut impl std::io::Write) -> Result<(), CodecError> {
807 let context = Context::new(self.is_some());
808 Boolean(self.is_some())
809 .encode(writer)
810 .map_err(|error| error.with_context(CodecKind::PrefixedOptional))?;
811 self.0
812 .encode_with_context(writer, &context)
813 .map_err(|error| error.with_context(CodecKind::PrefixedOptional))
814 }
815
816 fn decode(reader: &mut impl std::io::Read) -> Result<Self, CodecError> {
817 let present = Boolean::decode(reader)
818 .map_err(|error| error.with_context(CodecKind::PrefixedOptional))?;
819 Optional::decode_with_context(reader, &Context::new(present.0))
820 .map(Self)
821 .map_err(|error| error.with_context(CodecKind::PrefixedOptional))
822 }
823}
824
825/// A boolean-selected value of protocol type `X` or `Y`.
826///
827/// The wire representation begins with a [`Boolean`]. A true marker is
828/// followed by an `X` value, while a false marker is followed by a `Y` value:
829///
830/// ```text
831/// 0x01 + X
832/// 0x00 + Y
833/// ```
834///
835/// Both branch types must implement [`TypeCodec`]. The enum variant binds the
836/// marker to the matching payload, so an in-memory marker/payload mismatch
837/// cannot be represented.
838///
839/// # Examples
840///
841/// ```
842/// use mcproto_types::{Either, TypeCodec, UnsignedByte, VarInt};
843///
844/// let x = Either::<UnsignedByte, VarInt>::X(UnsignedByte(0xab));
845/// let mut encoded = Vec::new();
846/// x.encode(&mut encoded)?;
847/// assert_eq!(encoded, [0x01, 0xab]);
848/// assert_eq!(Either::decode(&mut encoded.as_slice())?, x);
849///
850/// let y = Either::<UnsignedByte, VarInt>::Y(VarInt(25565));
851/// let mut encoded = Vec::new();
852/// y.encode(&mut encoded)?;
853/// assert_eq!(encoded, [0x00, 0xdd, 0xc7, 0x01]);
854/// # Ok::<(), mcproto_codec::error::CodecError>(())
855/// ```
856///
857/// See the official [Either X or Y] protocol documentation.
858///
859/// [Either X or Y]: https://minecraft.wiki/w/Java_Edition_protocol/Packets#Either
860#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
861pub enum Either<X, Y> {
862 /// An `X` payload, prefixed by a true boolean.
863 X(X),
864 /// A `Y` payload, prefixed by a false boolean.
865 Y(Y),
866}
867
868impl<X, Y> Either<X, Y> {
869 /// Returns whether this value contains an `X` payload.
870 #[must_use]
871 pub const fn is_x(&self) -> bool {
872 matches!(self, Self::X(_))
873 }
874
875 /// Returns whether this value contains a `Y` payload.
876 #[must_use]
877 pub const fn is_y(&self) -> bool {
878 matches!(self, Self::Y(_))
879 }
880
881 /// Borrows the selected payload while retaining its branch.
882 #[must_use]
883 pub const fn as_ref(&self) -> Either<&X, &Y> {
884 match self {
885 Self::X(value) => Either::X(value),
886 Self::Y(value) => Either::Y(value),
887 }
888 }
889
890 /// Extracts the `X` payload, returning `None` for the `Y` branch.
891 #[must_use]
892 pub fn into_x(self) -> Option<X> {
893 match self {
894 Self::X(value) => Some(value),
895 Self::Y(_) => None,
896 }
897 }
898
899 /// Extracts the `Y` payload, returning `None` for the `X` branch.
900 #[must_use]
901 pub fn into_y(self) -> Option<Y> {
902 match self {
903 Self::X(_) => None,
904 Self::Y(value) => Some(value),
905 }
906 }
907}
908
909impl<X, Y> TypeCodec for Either<X, Y>
910where
911 X: TypeCodec,
912 Y: TypeCodec,
913{
914 fn encode(&self, writer: &mut impl std::io::Write) -> Result<(), CodecError> {
915 match self {
916 Self::X(value) => {
917 Boolean(true)
918 .encode(writer)
919 .map_err(|error| error.with_context(CodecKind::Either))?;
920 value
921 .encode(writer)
922 .map_err(|error| error.with_context(CodecKind::Either))
923 }
924 Self::Y(value) => {
925 Boolean(false)
926 .encode(writer)
927 .map_err(|error| error.with_context(CodecKind::Either))?;
928 value
929 .encode(writer)
930 .map_err(|error| error.with_context(CodecKind::Either))
931 }
932 }
933 }
934
935 fn decode(reader: &mut impl std::io::Read) -> Result<Self, CodecError> {
936 if Boolean::decode(reader)
937 .map_err(|error| error.with_context(CodecKind::Either))?
938 .0
939 {
940 X::decode(reader)
941 .map(Self::X)
942 .map_err(|error| error.with_context(CodecKind::Either))
943 } else {
944 Y::decode(reader)
945 .map(Self::Y)
946 .map_err(|error| error.with_context(CodecKind::Either))
947 }
948 }
949}
950
951/// A protocol value represented either by registry ID or by an inline `T`.
952///
953/// The [Minecraft protocol ID or X] wire representation begins with a
954/// [`VarInt`] selector:
955///
956/// - `0` means that a value of type `T` follows inline;
957/// - a positive value `n` refers to registry ID `n - 1` and has no inline
958/// payload.
959///
960/// ```text
961/// VarInt(0) + T // Inline value
962/// VarInt(registry_id + 1) // Registry reference
963/// ```
964///
965/// Registry IDs held by this type are the actual zero-based IDs, not their
966/// incremented wire selectors. Valid IDs range from `0` through
967/// `i32::MAX - 1`. The registry itself is implied by the enclosing packet or
968/// field definition; it does not need to be stored in [`Context`] because it
969/// does not affect this value's byte layout.
970///
971/// [Minecraft protocol ID or X]: https://minecraft.wiki/w/Java_Edition_protocol/Packets#ID_or_X
972/// [`VarInt`]: crate::basic::VarInt
973///
974/// # Examples
975///
976/// ```
977/// use mcproto_types::{TypeCodec, basic::UnsignedByte};
978/// use mcproto_types::contextual::IdOr;
979///
980/// let inline = IdOr::inline(UnsignedByte(0xab));
981/// let mut encoded = Vec::new();
982/// inline.encode(&mut encoded)?;
983/// assert_eq!(encoded, [0x00, 0xab]);
984///
985/// let mut input = encoded.as_slice();
986/// assert_eq!(IdOr::<UnsignedByte>::decode(&mut input)?, inline);
987///
988/// let reference = IdOr::<UnsignedByte>::id(4);
989/// let mut encoded = Vec::new();
990/// reference.encode(&mut encoded)?;
991/// assert_eq!(encoded, [0x05]);
992/// # Ok::<(), mcproto_codec::error::CodecError>(())
993/// ```
994#[derive(Debug, Clone, PartialEq, Eq, Hash)]
995pub enum IdOr<T> {
996 /// A zero-based ID in the registry implied by the enclosing field.
997 Id(i32),
998 /// A complete value encoded inline after a zero selector.
999 Inline(T),
1000}
1001
1002impl<T> IdOr<T> {
1003 /// Creates a registry reference from its actual zero-based ID.
1004 #[must_use]
1005 pub const fn id(id: i32) -> Self {
1006 Self::Id(id)
1007 }
1008
1009 /// Creates an inline value.
1010 #[must_use]
1011 pub const fn inline(value: T) -> Self {
1012 Self::Inline(value)
1013 }
1014
1015 /// Returns whether this value is a registry reference.
1016 #[must_use]
1017 pub const fn is_id(&self) -> bool {
1018 matches!(self, Self::Id(_))
1019 }
1020
1021 /// Returns whether this value is defined inline.
1022 #[must_use]
1023 pub const fn is_inline(&self) -> bool {
1024 matches!(self, Self::Inline(_))
1025 }
1026
1027 /// Returns the zero-based registry ID, if this is a reference.
1028 #[must_use]
1029 pub const fn registry_id(&self) -> Option<i32> {
1030 match self {
1031 Self::Id(id) => Some(*id),
1032 Self::Inline(_) => None,
1033 }
1034 }
1035
1036 /// Returns the inline value by reference, if present.
1037 #[must_use]
1038 pub const fn inline_value(&self) -> Option<&T> {
1039 match self {
1040 Self::Id(_) => None,
1041 Self::Inline(value) => Some(value),
1042 }
1043 }
1044
1045 /// Borrows the inline value while preserving registry references.
1046 #[must_use]
1047 pub const fn as_ref(&self) -> IdOr<&T> {
1048 match self {
1049 Self::Id(id) => IdOr::Id(*id),
1050 Self::Inline(value) => IdOr::Inline(value),
1051 }
1052 }
1053
1054 /// Extracts the inline value, if present.
1055 #[must_use]
1056 pub fn into_inline(self) -> Option<T> {
1057 match self {
1058 Self::Id(_) => None,
1059 Self::Inline(value) => Some(value),
1060 }
1061 }
1062}
1063
1064impl<T> From<T> for IdOr<T> {
1065 fn from(value: T) -> Self {
1066 Self::Inline(value)
1067 }
1068}
1069
1070impl<T> TypeCodec for IdOr<T>
1071where
1072 T: TypeCodec,
1073{
1074 fn encode(&self, writer: &mut impl std::io::Write) -> Result<(), CodecError> {
1075 match self {
1076 Self::Id(id) => {
1077 let selector = id.checked_add(1).filter(|_| *id >= 0).ok_or_else(|| {
1078 CodecError::invalid_encoding_for_operation(
1079 CodecKind::IdOr,
1080 CodecOperation::Write,
1081 0,
1082 InvalidEncodingReason::InvalidRegistryId {
1083 value: *id,
1084 max: i32::MAX - 1,
1085 },
1086 )
1087 })?;
1088 writer
1089 .write_varint(selector)
1090 .map_err(|error| error.with_context(CodecKind::IdOr))
1091 }
1092 Self::Inline(value) => {
1093 writer
1094 .write_varint(0)
1095 .map_err(|error| error.with_context(CodecKind::IdOr))?;
1096 value
1097 .encode(writer)
1098 .map_err(|error| error.with_context(CodecKind::IdOr))
1099 }
1100 }
1101 }
1102
1103 fn decode(reader: &mut impl std::io::Read) -> Result<Self, CodecError> {
1104 let (selector, prefix_size) = reader
1105 .read_varint_with_size()
1106 .map_err(|error| error.with_context(CodecKind::IdOr))?;
1107 match selector {
1108 0 => T::decode(reader)
1109 .map(Self::Inline)
1110 .map_err(|error| error.with_context(CodecKind::IdOr)),
1111 1.. => Ok(Self::Id(selector - 1)),
1112 _ => Err(CodecError::invalid_encoding(
1113 CodecKind::IdOr,
1114 prefix_size,
1115 InvalidEncodingReason::InvalidIdOrSelector { value: selector },
1116 )),
1117 }
1118 }
1119}
1120
1121/// A set of registry IDs represented inline or by reference to a tag.
1122///
1123/// The registry itself is implied by the enclosing packet or field. The
1124/// [Minecraft protocol ID Set] wire representation starts with a [`VarInt`]
1125/// type value:
1126///
1127/// - `0` is followed by an [`Identifier`] naming a registry tag;
1128/// - a positive value `n` is followed by `n - 1` registry IDs encoded as
1129/// VarInts.
1130///
1131/// ```text
1132/// VarInt(0) + Identifier(tag_name)
1133/// VarInt(ids.len() + 1) + VarInt(ids[0]) + ... + VarInt(ids[len - 1])
1134/// ```
1135///
1136/// An empty inline set therefore uses type value `1`. Registry IDs must be
1137/// non-negative, and an inline set may contain at most `i32::MAX - 1` IDs.
1138///
1139/// [Minecraft protocol ID Set]: https://minecraft.wiki/w/Java_Edition_protocol/Packets#ID_Set
1140/// [`VarInt`]: crate::basic::VarInt
1141///
1142/// # Examples
1143///
1144/// ```
1145/// use mcproto_types::{TypeCodec, basic::Identifier};
1146/// use mcproto_types::contextual::IdSet;
1147///
1148/// let inline = IdSet::inline(vec![3, 7]);
1149/// let mut encoded = Vec::new();
1150/// inline.encode(&mut encoded)?;
1151/// assert_eq!(encoded, [0x03, 0x03, 0x07]);
1152///
1153/// let mut input = encoded.as_slice();
1154/// assert_eq!(IdSet::decode(&mut input)?, inline);
1155///
1156/// let tagged = IdSet::tag(Identifier::new("minecraft:logs")?);
1157/// assert!(tagged.is_tag());
1158/// # Ok::<(), Box<dyn std::error::Error>>(())
1159/// ```
1160#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1161pub enum IdSet {
1162 /// A named set of IDs defined by a registry tag.
1163 Tag(Identifier),
1164 /// An ad-hoc set of zero-based registry IDs enumerated inline.
1165 Inline(Vec<i32>),
1166}
1167
1168impl IdSet {
1169 /// Creates an ID set that refers to a registry tag.
1170 #[must_use]
1171 pub const fn tag(tag_name: Identifier) -> Self {
1172 Self::Tag(tag_name)
1173 }
1174
1175 /// Creates an ID set containing inline registry IDs.
1176 #[must_use]
1177 pub const fn inline(ids: Vec<i32>) -> Self {
1178 Self::Inline(ids)
1179 }
1180
1181 /// Returns whether this set refers to a registry tag.
1182 #[must_use]
1183 pub const fn is_tag(&self) -> bool {
1184 matches!(self, Self::Tag(_))
1185 }
1186
1187 /// Returns whether this set enumerates registry IDs inline.
1188 #[must_use]
1189 pub const fn is_inline(&self) -> bool {
1190 matches!(self, Self::Inline(_))
1191 }
1192
1193 /// Returns the registry tag name, if this is a tag reference.
1194 #[must_use]
1195 pub const fn tag_name(&self) -> Option<&Identifier> {
1196 match self {
1197 Self::Tag(tag_name) => Some(tag_name),
1198 Self::Inline(_) => None,
1199 }
1200 }
1201
1202 /// Returns the inline registry IDs, if present.
1203 #[must_use]
1204 pub const fn ids(&self) -> Option<&[i32]> {
1205 match self {
1206 Self::Tag(_) => None,
1207 Self::Inline(ids) => Some(ids.as_slice()),
1208 }
1209 }
1210
1211 /// Extracts the registry tag name, if this is a tag reference.
1212 #[must_use]
1213 pub fn into_tag(self) -> Option<Identifier> {
1214 match self {
1215 Self::Tag(tag_name) => Some(tag_name),
1216 Self::Inline(_) => None,
1217 }
1218 }
1219
1220 /// Extracts the inline registry IDs, if present.
1221 #[must_use]
1222 pub fn into_ids(self) -> Option<Vec<i32>> {
1223 match self {
1224 Self::Tag(_) => None,
1225 Self::Inline(ids) => Some(ids),
1226 }
1227 }
1228}
1229
1230impl From<Identifier> for IdSet {
1231 fn from(tag_name: Identifier) -> Self {
1232 Self::Tag(tag_name)
1233 }
1234}
1235
1236impl From<Vec<i32>> for IdSet {
1237 fn from(ids: Vec<i32>) -> Self {
1238 Self::Inline(ids)
1239 }
1240}
1241
1242impl TypeCodec for IdSet {
1243 fn encode(&self, writer: &mut impl std::io::Write) -> Result<(), CodecError> {
1244 match self {
1245 Self::Tag(tag_name) => {
1246 writer
1247 .write_varint(0)
1248 .map_err(|error| error.with_context(CodecKind::IdSet))?;
1249 tag_name
1250 .encode(writer)
1251 .map_err(|error| error.with_context(CodecKind::IdSet))
1252 }
1253 Self::Inline(ids) => {
1254 if let Some(id) = ids.iter().copied().find(|id| *id < 0) {
1255 return Err(CodecError::invalid_encoding_for_operation(
1256 CodecKind::IdSet,
1257 CodecOperation::Write,
1258 0,
1259 InvalidEncodingReason::InvalidRegistryId {
1260 value: id,
1261 max: i32::MAX,
1262 },
1263 ));
1264 }
1265 let type_value = i32::try_from(ids.len())
1266 .ok()
1267 .and_then(|length| length.checked_add(1))
1268 .ok_or_else(|| {
1269 CodecError::invalid_encoding_for_operation(
1270 CodecKind::IdSet,
1271 CodecOperation::Write,
1272 0,
1273 InvalidEncodingReason::LengthOutOfRange {
1274 max: (i32::MAX - 1) as usize,
1275 actual: ids.len(),
1276 },
1277 )
1278 })?;
1279
1280 writer
1281 .write_varint(type_value)
1282 .map_err(|error| error.with_context(CodecKind::IdSet))?;
1283 for id in ids {
1284 writer
1285 .write_varint(*id)
1286 .map_err(|error| error.with_context(CodecKind::IdSet))?;
1287 }
1288 Ok(())
1289 }
1290 }
1291 }
1292
1293 fn decode(reader: &mut impl std::io::Read) -> Result<Self, CodecError> {
1294 let (type_value, type_size) = reader
1295 .read_varint_with_size()
1296 .map_err(|error| error.with_context(CodecKind::IdSet))?;
1297 match type_value {
1298 0 => Identifier::decode(reader)
1299 .map(Self::Tag)
1300 .map_err(|error| error.with_context(CodecKind::IdSet)),
1301 1.. => {
1302 let length = (type_value - 1) as usize;
1303 let mut bytes_processed = type_size;
1304 let mut ids = Vec::new();
1305 for _ in 0..length {
1306 let (id, id_size) = reader
1307 .read_varint_with_size()
1308 .map_err(|error| error.with_context(CodecKind::IdSet))?;
1309 bytes_processed += id_size;
1310 if id < 0 {
1311 return Err(CodecError::invalid_encoding(
1312 CodecKind::IdSet,
1313 bytes_processed,
1314 InvalidEncodingReason::InvalidRegistryId {
1315 value: id,
1316 max: i32::MAX,
1317 },
1318 ));
1319 }
1320 ids.push(id);
1321 }
1322 Ok(Self::Inline(ids))
1323 }
1324 _ => Err(CodecError::invalid_encoding(
1325 CodecKind::IdSet,
1326 type_size,
1327 InvalidEncodingReason::InvalidIdSetType { value: type_value },
1328 )),
1329 }
1330 }
1331}