Skip to main content

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}