Skip to content

Commit e4131a1

Browse files
authored
Filter top-level component deserialization for messages (#3127)
Adds a safeguard for when components v2 gets enabled, which would otherwise break assumptions we make when deserializing. Adding full support would require API breakage, so a stopgap workaround is needed on `current`.
1 parent 1e4e5a3 commit e4131a1

File tree

2 files changed

+57
-3
lines changed

2 files changed

+57
-3
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ zerofrom = "=0.1.5"
3434

3535
# Required dependencies
3636
bitflags = "2.4.2"
37-
serde_json = "1.0.108"
37+
serde_json = { version = "1.0.108", features = ["raw_value"] }
3838
async-trait = "0.1.74"
3939
tracing = { version = "0.1.40", features = ["log"] }
4040
serde = { version = "1.0.192", features = ["derive"] }

src/model/channel/message.rs

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ pub struct Message {
118118
/// The thread that was started from this message, includes thread member object.
119119
pub thread: Option<GuildChannel>,
120120
/// The components of this message
121-
#[serde(default)]
121+
#[serde(default, deserialize_with = "deserialize_components")]
122122
pub components: Vec<ActionRow>,
123123
/// Array of message sticker item objects.
124124
#[serde(default)]
@@ -151,6 +151,60 @@ pub struct Message {
151151
pub poll: Option<Box<Poll>>,
152152
}
153153

154+
// Custom deserialize function to deserialize components safely without knocking the whole message
155+
// out when new components are found but not supported.
156+
fn deserialize_components<'de, D>(deserializer: D) -> Result<Vec<ActionRow>, D::Error>
157+
where
158+
D: Deserializer<'de>,
159+
{
160+
#[derive(Deserialize)]
161+
struct MinComponent {
162+
#[serde(rename = "type")]
163+
kind: u8,
164+
}
165+
166+
struct ComponentsVisitor;
167+
168+
impl<'de> Visitor<'de> for ComponentsVisitor {
169+
type Value = Vec<ActionRow>;
170+
171+
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172+
formatter.write_str("a sequence of ActionRow elements")
173+
}
174+
175+
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
176+
where
177+
A: serde::de::SeqAccess<'de>,
178+
{
179+
let mut components = Vec::with_capacity(seq.size_hint().unwrap_or_default());
180+
181+
while let Some(raw) = seq.next_element::<&serde_json::value::RawValue>()? {
182+
// We deserialize only the `kind` field to determine the component type.
183+
// We later use this to check if its a supported component before deserializing the
184+
// entire payload.
185+
let min_component =
186+
MinComponent::deserialize(raw).map_err(serde::de::Error::custom)?;
187+
188+
// Action rows are the only top level component supported in serenity at this time.
189+
if min_component.kind == 1 {
190+
components.push(ActionRow::deserialize(raw).map_err(serde::de::Error::custom)?);
191+
} else {
192+
// Top level component is not an action row and cannot be supported on
193+
// serenity@current without breaking changes, so we skip them.
194+
tracing::debug!(
195+
"Skipping component with unsupported kind: {}",
196+
min_component.kind
197+
);
198+
}
199+
}
200+
201+
Ok(components)
202+
}
203+
}
204+
205+
deserializer.deserialize_seq(ComponentsVisitor)
206+
}
207+
154208
#[cfg(feature = "model")]
155209
impl Message {
156210
/// Crossposts this message.
@@ -1211,7 +1265,7 @@ pub struct MessageSnapshot {
12111265
#[serde(rename = "type")]
12121266
pub kind: MessageType,
12131267
pub flags: Option<MessageFlags>,
1214-
#[serde(default)]
1268+
#[serde(default, deserialize_with = "deserialize_components")]
12151269
pub components: Vec<ActionRow>,
12161270
#[serde(default)]
12171271
pub sticker_items: Vec<StickerItem>,

0 commit comments

Comments
 (0)