Skip to main content

mcproto_types/debug/
update.rs

1//! Debug subscription updates.
2
3use std::io::{Read, Write};
4
5use mcproto_codec::error::{CodecError, CodecKind};
6
7use crate::{Boolean, TypeCodec};
8
9use super::{DebugSubscriptionData, DebugSubscriptionType};
10
11/// A debug subscription type followed by prefixed optional matching data.
12///
13/// [`Absent`](Self::Absent) writes the type and a false boolean. A
14/// [`Present`](Self::Present) value writes the payload's type, a true boolean,
15/// and the matching payload. This representation cannot pair one subscription
16/// type with another type's data.
17///
18/// # Examples
19///
20/// ```
21/// use mcproto_types::{
22///     DebugSubscriptionData, DebugSubscriptionType, DebugSubscriptionUpdate, TypeCodec,
23/// };
24///
25/// let absent = DebugSubscriptionUpdate::absent(DebugSubscriptionType::Bee);
26/// let mut encoded = Vec::new();
27/// absent.encode(&mut encoded)?;
28/// assert_eq!(encoded, [0x01, 0x00]);
29/// assert_eq!(DebugSubscriptionUpdate::decode(&mut encoded.as_slice())?, absent);
30///
31/// let present = DebugSubscriptionUpdate::present(
32///     DebugSubscriptionData::DedicatedServerTickTime,
33/// );
34/// let mut encoded = Vec::new();
35/// present.encode(&mut encoded)?;
36/// assert_eq!(encoded, [0x00, 0x01]);
37/// # Ok::<(), mcproto_codec::error::CodecError>(())
38/// ```
39///
40/// See the official [Debug Subscription Update] documentation.
41///
42/// [Debug Subscription Update]: https://minecraft.wiki/w/Java_Edition_protocol/Packets#Debug_Subscription_Update
43#[derive(Debug, Clone, PartialEq)]
44pub enum DebugSubscriptionUpdate {
45    /// The selected subscription type has no following payload.
46    Absent(DebugSubscriptionType),
47    /// The selected subscription type is followed by its payload.
48    Present(DebugSubscriptionData),
49}
50
51impl DebugSubscriptionUpdate {
52    /// Creates an update with no payload.
53    #[must_use]
54    pub const fn absent(subscription_type: DebugSubscriptionType) -> Self {
55        Self::Absent(subscription_type)
56    }
57
58    /// Creates an update containing matching typed data.
59    #[must_use]
60    pub const fn present(data: DebugSubscriptionData) -> Self {
61        Self::Present(data)
62    }
63
64    /// Returns the selected subscription type.
65    #[must_use]
66    pub const fn subscription_type(&self) -> DebugSubscriptionType {
67        match self {
68            Self::Absent(subscription_type) => *subscription_type,
69            Self::Present(data) => data.subscription_type(),
70        }
71    }
72
73    /// Returns the payload when present.
74    #[must_use]
75    pub const fn data(&self) -> Option<&DebugSubscriptionData> {
76        match self {
77            Self::Absent(_) => None,
78            Self::Present(data) => Some(data),
79        }
80    }
81
82    /// Returns whether this update contains a payload.
83    #[must_use]
84    pub const fn is_present(&self) -> bool {
85        matches!(self, Self::Present(_))
86    }
87}
88
89impl TypeCodec for DebugSubscriptionUpdate {
90    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
91        self.subscription_type()
92            .encode(writer)
93            .map_err(|error| error.with_context(CodecKind::DebugSubscriptionUpdate))?;
94
95        let data = self.data();
96        Boolean(data.is_some())
97            .encode(writer)
98            .map_err(|error| error.with_context(CodecKind::DebugSubscriptionUpdate))?;
99        if let Some(data) = data {
100            data.encode_payload(writer)
101                .map_err(|error| error.with_context(CodecKind::DebugSubscriptionUpdate))?;
102        }
103        Ok(())
104    }
105
106    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
107        let subscription_type = DebugSubscriptionType::decode(reader)
108            .map_err(|error| error.with_context(CodecKind::DebugSubscriptionUpdate))?;
109        let present = Boolean::decode(reader)
110            .map_err(|error| error.with_context(CodecKind::DebugSubscriptionUpdate))?;
111        if present.0 {
112            DebugSubscriptionData::decode_payload(subscription_type, reader)
113                .map(Self::Present)
114                .map_err(|error| error.with_context(CodecKind::DebugSubscriptionUpdate))
115        } else {
116            Ok(Self::Absent(subscription_type))
117        }
118    }
119}