Skip to content

Commit 340a582

Browse files
bors[bot]Nerixyz
andauthored
Merge #260
260: feat: provide `to_str` and `FromStr` for EventType r=Nerixyz a=Nerixyz To check the `Twitch-Eventsub-Subscription-Type` header, I need to compare it to the `EventType`. When creating a guard for an eventsub route, the `EventType` is already known, so the guard should just compare strings/bytes. However, the only way of getting the string representation is through `impl Display`, which allocates. That's why I'm adding `to_str` (which is marked as const so rustc hopefully computes the output at compile time in generic functions). When adding `to_str` we might as well add `FromStr`, where I didn't really have an idea about the error type, so it's like an `Option` now. Since the implementations for `to_str` and `FromStr` require a lot of repeating code, I created a macro that implements the whole type. The type wasn't marked as `Copy`, so I marked it as `Copy` since it's really just an integer in the end. Co-authored-by: Nerixyz <[email protected]>
2 parents 607b9ae + 24d6190 commit 340a582

File tree

2 files changed

+134
-108
lines changed

2 files changed

+134
-108
lines changed

src/eventsub/event.rs

Lines changed: 134 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -52,111 +52,141 @@ macro_rules! is_thing {
5252
};
5353
}
5454

55-
/// Event types
56-
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
57-
#[cfg_attr(feature = "deny_unknown_fields", serde(deny_unknown_fields))]
58-
#[non_exhaustive]
59-
pub enum EventType {
60-
/// `channel.update` subscription type sends notifications when a broadcaster updates the category, title, mature flag, or broadcast language for their channel.
61-
#[serde(rename = "channel.update")]
62-
ChannelUpdate,
63-
/// `channel.follow`: a specified channel receives a follow.
64-
#[serde(rename = "channel.follow")]
65-
ChannelFollow,
66-
/// `channel.subscribe`: a specified channel receives a subscriber. This does not include resubscribes.
67-
#[serde(rename = "channel.subscribe")]
68-
ChannelSubscribe,
69-
/// `channel.cheer`: a user cheers on the specified channel.
70-
#[serde(rename = "channel.cheer")]
71-
ChannelCheer,
72-
/// `channel.ban`: a viewer is banned from the specified channel.
73-
#[serde(rename = "channel.ban")]
74-
ChannelBan,
75-
/// `channel.unban`: a viewer is unbanned from the specified channel.
76-
#[serde(rename = "channel.unban")]
77-
ChannelUnban,
78-
/// `channel.channel_points_custom_reward.add`: a custom channel points reward has been created for the specified channel.
79-
#[serde(rename = "channel.channel_points_custom_reward.add")]
80-
ChannelPointsCustomRewardAdd,
81-
/// `channel.channel_points_custom_reward.update`: a custom channel points reward has been updated for the specified channel.
82-
#[serde(rename = "channel.channel_points_custom_reward.update")]
83-
ChannelPointsCustomRewardUpdate,
84-
/// `channel.channel_points_custom_reward.remove`: a custom channel points reward has been removed from the specified channel.
85-
#[serde(rename = "channel.channel_points_custom_reward.remove")]
86-
ChannelPointsCustomRewardRemove,
87-
/// `channel.channel_points_custom_reward_redemption.add`: a viewer has redeemed a custom channel points reward on the specified channel.
88-
#[serde(rename = "channel.channel_points_custom_reward_redemption.add")]
89-
ChannelPointsCustomRewardRedemptionAdd,
90-
/// `channel.channel_points_custom_reward_redemption.update`: a redemption of a channel points custom reward has been updated for the specified channel.
91-
#[serde(rename = "channel.channel_points_custom_reward_redemption.update")]
92-
ChannelPointsCustomRewardRedemptionUpdate,
93-
/// `channel.poll.begin`: a poll begins on the specified channel.
94-
#[serde(rename = "channel.poll.begin")]
95-
ChannelPollBegin,
96-
/// `channel.poll.progress`: a user responds to a poll on the specified channel.
97-
#[serde(rename = "channel.poll.progress")]
98-
ChannelPollProgress,
99-
/// `channel.poll.end`: a poll ends on the specified channel.
100-
#[serde(rename = "channel.poll.end")]
101-
ChannelPollEnd,
102-
/// `channel.prediction.begin`: a Prediction begins on the specified channel
103-
#[serde(rename = "channel.prediction.begin")]
104-
ChannelPredictionBegin,
105-
/// `channel.prediction.progress`: a user participates in a Prediction on the specified channel.
106-
#[serde(rename = "channel.prediction.progress")]
107-
ChannelPredictionProgress,
108-
/// `channel.prediction.lock`: a Prediction is locked on the specified channel.
109-
#[serde(rename = "channel.prediction.lock")]
110-
ChannelPredictionLock,
111-
/// `channel.prediction.end`: a Prediction ends on the specified channel.
112-
#[serde(rename = "channel.prediction.end")]
113-
ChannelPredictionEnd,
114-
/// `channel.raid`: a broadcaster raids another broadcaster’s channel.
115-
#[serde(rename = "channel.raid")]
116-
ChannelRaid,
117-
/// `channel.subscription.end`: a subscription to the specified channel expires.
118-
#[serde(rename = "channel.subscription.end")]
119-
ChannelSubscriptionEnd,
120-
/// `channel.subscription.gift`: a user gives one or more gifted subscriptions in a channel.
121-
#[serde(rename = "channel.subscription.gift")]
122-
ChannelSubscriptionGift,
123-
/// `channel.subscription.gift`: a user sends a resubscription chat message in a specific channel
124-
#[serde(rename = "channel.subscription.message")]
125-
ChannelSubscriptionMessage,
126-
/// `channel.goal.begin`: a goal begins on the specified channel.
127-
#[serde(rename = "channel.goal.begin")]
128-
ChannelGoalBegin,
129-
/// `channel.goal.progress`: a goal makes progress on the specified channel.
130-
#[serde(rename = "channel.goal.progress")]
131-
ChannelGoalProgress,
132-
/// `channel.goal.end`: a goal ends on the specified channel.
133-
#[serde(rename = "channel.goal.end")]
134-
ChannelGoalEnd,
135-
/// `channel.hype_train.begin`: a hype train begins on the specified channel.
136-
#[serde(rename = "channel.hype_train.begin")]
137-
ChannelHypeTrainBegin,
138-
/// `channel.hype_train.progress`: a hype train makes progress on the specified channel.
139-
#[serde(rename = "channel.hype_train.progress")]
140-
ChannelHypeTrainProgress,
141-
/// `channel.hype_train.end`: a hype train ends on the specified channel.
142-
#[serde(rename = "channel.hype_train.end")]
143-
ChannelHypeTrainEnd,
144-
/// `stream.online`: the specified broadcaster starts a stream.
145-
#[serde(rename = "stream.online")]
146-
StreamOnline,
147-
/// `stream.online`: the specified broadcaster stops a stream.
148-
#[serde(rename = "stream.offline")]
149-
StreamOffline,
150-
/// `user.update`: user updates their account.
151-
#[serde(rename = "user.update")]
152-
UserUpdate,
153-
/// `user.authorization.revoke`: a user has revoked authorization for your client id. Use this webhook to meet government requirements for handling user data, such as GDPR, LGPD, or CCPA.
154-
#[serde(rename = "user.authorization.revoke")]
155-
UserAuthorizationRevoke,
156-
/// `user.authorization.revoke`: a user’s authorization has been granted to your client id.
157-
#[serde(rename = "user.authorization.grant")]
158-
UserAuthorizationGrant,
55+
macro_rules! make_event_type {
56+
($enum_docs:literal: pub enum $enum_name:ident {
57+
$(
58+
$event_docs:literal:
59+
$variant_name:ident => $event_name:literal,
60+
)*
61+
},
62+
to_str: $to_str_docs:literal,
63+
from_str_error: $from_str_error:ident,
64+
) => {
65+
#[doc = $enum_docs]
66+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
67+
#[cfg_attr(feature = "deny_unknown_fields", serde(deny_unknown_fields))]
68+
#[non_exhaustive]
69+
pub enum $enum_name {
70+
$(
71+
#[doc = concat!("`", $event_name, "`: ", $event_docs)]
72+
#[serde(rename = $event_name)]
73+
$variant_name,
74+
)*
75+
}
76+
77+
impl $enum_name {
78+
#[doc = $to_str_docs]
79+
pub const fn to_str(&self) -> &'static str {
80+
use $enum_name::*;
81+
match self {
82+
$($variant_name => $event_name,)*
83+
}
84+
}
85+
}
86+
87+
impl std::str::FromStr for $enum_name {
88+
type Err = $from_str_error;
89+
90+
fn from_str(s: &str) -> Result<Self, Self::Err> {
91+
use $enum_name::*;
92+
match s {
93+
$($event_name => Ok($variant_name),)*
94+
_ => Err($from_str_error),
95+
}
96+
}
97+
}
98+
99+
impl std::fmt::Display for $enum_name {
100+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101+
f.write_str(self.to_str())
102+
}
103+
}
104+
};
105+
}
106+
107+
/// Error when parsing an event-type string.
108+
#[derive(thiserror::Error, Debug, Clone)]
109+
#[error("Unknown event type")]
110+
pub struct EventTypeParseError;
111+
112+
make_event_type!("Event Types": pub enum EventType {
113+
"subscription type sends notifications when a broadcaster updates the category, title, mature flag, or broadcast language for their channel.":
114+
ChannelUpdate => "channel.update",
115+
"a specified channel receives a follow.":
116+
ChannelFollow => "channel.follow",
117+
"a specified channel receives a subscriber. This does not include resubscribes.":
118+
ChannelSubscribe => "channel.subscribe",
119+
"a user cheers on the specified channel.":
120+
ChannelCheer => "channel.cheer",
121+
"a viewer is banned from the specified channel.":
122+
ChannelBan => "channel.ban",
123+
"a viewer is unbanned from the specified channel.":
124+
ChannelUnban => "channel.unban",
125+
"a custom channel points reward has been created for the specified channel.":
126+
ChannelPointsCustomRewardAdd => "channel.channel_points_custom_reward.add",
127+
"a custom channel points reward has been updated for the specified channel.":
128+
ChannelPointsCustomRewardUpdate => "channel.channel_points_custom_reward.update",
129+
"a custom channel points reward has been removed from the specified channel.":
130+
ChannelPointsCustomRewardRemove => "channel.channel_points_custom_reward.remove",
131+
"a viewer has redeemed a custom channel points reward on the specified channel.":
132+
ChannelPointsCustomRewardRedemptionAdd => "channel.channel_points_custom_reward_redemption.add",
133+
"a redemption of a channel points custom reward has been updated for the specified channel.":
134+
ChannelPointsCustomRewardRedemptionUpdate => "channel.channel_points_custom_reward_redemption.update",
135+
"a poll begins on the specified channel.":
136+
ChannelPollBegin => "channel.poll.begin",
137+
"a user responds to a poll on the specified channel.":
138+
ChannelPollProgress => "channel.poll.progress",
139+
"a poll ends on the specified channel.":
140+
ChannelPollEnd => "channel.poll.end",
141+
"a Prediction begins on the specified channel":
142+
ChannelPredictionBegin => "channel.prediction.begin",
143+
"a user participates in a Prediction on the specified channel.":
144+
ChannelPredictionProgress => "channel.prediction.progress",
145+
"a Prediction is locked on the specified channel.":
146+
ChannelPredictionLock => "channel.prediction.lock",
147+
"a Prediction ends on the specified channel.":
148+
ChannelPredictionEnd => "channel.prediction.end",
149+
"a broadcaster raids another broadcaster’s channel.":
150+
ChannelRaid => "channel.raid",
151+
"a subscription to the specified channel expires.":
152+
ChannelSubscriptionEnd => "channel.subscription.end",
153+
"a user gives one or more gifted subscriptions in a channel.":
154+
ChannelSubscriptionGift => "channel.subscription.gift",
155+
"a user sends a resubscription chat message in a specific channel":
156+
ChannelSubscriptionMessage => "channel.subscription.message",
157+
"a goal begins on the specified channel.":
158+
ChannelGoalBegin => "channel.goal.begin",
159+
"a goal makes progress on the specified channel.":
160+
ChannelGoalProgress => "channel.goal.progress",
161+
"a goal ends on the specified channel.":
162+
ChannelGoalEnd => "channel.goal.end",
163+
"a hype train begins on the specified channel.":
164+
ChannelHypeTrainBegin => "channel.hype_train.begin",
165+
"a hype train makes progress on the specified channel.":
166+
ChannelHypeTrainProgress => "channel.hype_train.progress",
167+
"a hype train ends on the specified channel.":
168+
ChannelHypeTrainEnd => "channel.hype_train.end",
169+
"the specified broadcaster starts a stream.":
170+
StreamOnline => "stream.online",
171+
"the specified broadcaster stops a stream.":
172+
StreamOffline => "stream.offline",
173+
"user updates their account.":
174+
UserUpdate => "user.update",
175+
"a user has revoked authorization for your client id. Use this webhook to meet government requirements for handling user data, such as GDPR, LGPD, or CCPA.":
176+
UserAuthorizationRevoke => "user.authorization.revoke",
177+
"a user’s authorization has been granted to your client id.":
178+
UserAuthorizationGrant => "user.authorization.grant",
179+
},
180+
to_str: r#"Get the event string of this event.
181+
```
182+
# use twitch_api::eventsub::EventType;
183+
fn main() {
184+
assert_eq!(EventType::ChannelUpdate.to_str(), "channel.update");
185+
assert_eq!(EventType::ChannelUnban.to_str(), "channel.unban");
159186
}
187+
```"#,
188+
from_str_error: EventTypeParseError,
189+
);
160190

161191
/// A notification with an event payload. Enumerates all possible [`Payload`s](Payload)
162192
///

src/eventsub/mod.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -398,10 +398,6 @@ pub enum TransportMethod {
398398
Webhook,
399399
}
400400

401-
impl std::fmt::Display for EventType {
402-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.serialize(f) }
403-
}
404-
405401
/// Subscription request status
406402
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
407403
#[non_exhaustive]

0 commit comments

Comments
 (0)