1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
//! Module for all possible scopes in twitch.
pub mod validator;
pub use validator::Validator;

use serde_derive::{Deserialize, Serialize};
use std::borrow::Cow;

macro_rules! scope_impls {
    (@omit #[deprecated($depr:tt)] $i:ident) => {
        #[cfg(_internal_never)]
        Self::$i
    };
    (@omit $i:ident) => {
        Self::$i
    };

    ($($(#[cfg(($cfg:meta))])* $(#[deprecated($depr:meta)])? $i:ident,scope: $rename:literal, doc: $doc:literal);* $(;)? ) => {
        #[doc = "Scopes for twitch."]
        #[doc = ""]
        #[doc = "<https://dev.twitch.tv/docs/authentication/scopes/>"]
        #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
        #[non_exhaustive]
        #[serde(from = "String")]
        #[serde(into = "String")]
        pub enum Scope {
            $(
                $(#[cfg($cfg)])*
                $(#[deprecated($depr)])*
                #[doc = $doc]
                #[doc = "\n\n"]
                #[doc = "`"]
                #[doc = $rename]
                #[doc = "`"]
                #[serde(rename = $rename)] // Is this even needed?
                $i,
            )*
            #[doc = "Other scope that is not implemented."]
            Other(Cow<'static, str>),
        }

        impl std::fmt::Display for Scope {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                #![allow(deprecated)]

                f.write_str(match self {
                    Scope::Other(s) => &s,
                    $(
                        $(#[cfg($cfg)])*
                        Scope::$i => $rename,
                    )*
                })
            }
        }

        impl Scope {
            #[doc = "Get a vec of all defined twitch [Scopes][Scope]."]
            #[doc = "\n\n"]
            #[doc = "Please note that this may not work for you, as some auth flows and \"apis\" don't accept all scopes"]
            pub fn all() -> Vec<Scope> {
                vec![
                    $(
                        scope_impls!(@omit $(#[deprecated($depr)])* $i),
                    )*
                ]
            }

            #[doc = "Get a slice of all defined twitch [Scopes][Scope]."]
            #[doc = "\n\n"]
            #[doc = "Please note that this may not work for you, as some auth flows and \"apis\" don't accept all scopes"]
            pub const fn all_slice() -> &'static [Scope] {
                &[
                    $(
                        scope_impls!(@omit $(#[deprecated($depr)])* $i),
                    )*
                ]
            }

            #[doc = "Get a description for the token"]
            pub const fn description(&self) -> &'static str {
                #![allow(deprecated)]

                match self {

                    $(
                        $(#[cfg($cfg)])*
                        Self::$i => $doc,
                    )*
                    _ => "unknown scope"
                }
            }

            #[doc = "Make a scope from a cow string"]
            pub fn parse<C>(s: C) -> Scope where C: Into<Cow<'static, str>> {
                #![allow(deprecated)]
                use std::borrow::Borrow;
                let s = s.into();
                match s.borrow() {

                    $(
                        $(#[cfg($cfg)])*
                        $rename => {Scope::$i}
                    )*,
                    _ => Scope::Other(s)
                }
            }

            /// Get the scope as a borrowed string.
            pub fn as_str(&self) -> &str {
                #![allow(deprecated)]
                match self {
                    $(
                        $(#[cfg($cfg)])*
                        Scope::$i => $rename,
                    )*
                    Self::Other(c) => c.as_ref()
                }
            }

            /// Get the scope as a static string slice.
            ///
            /// ## Panics
            ///
            /// This function panics if the scope can't be represented as a static string slice
            pub const fn as_static_str(&self) -> &'static str {
                #![allow(deprecated)]
                match self {
                    $(
                        $(#[cfg($cfg)])*
                        Scope::$i => $rename,
                    )*
                    Self::Other(Cow::Borrowed(s)) => s,
                    _ => panic!(),
                }
            }
        }
        #[test]
        #[cfg(test)]
        fn sorted() {
            let slice = [$(
                $(#[cfg($cfg)])*
                $rename,
            )*];
            let mut slice_sorted = [$(
                $(#[cfg($cfg)])*
                $rename,
            )*];
            slice_sorted.sort();
            for (scope, sorted) in slice.iter().zip(slice_sorted.iter()) {
                assert_eq!(scope, sorted);
            }
            assert_eq!(slice, slice_sorted);
        }
    };
}

scope_impls!(
    AnalyticsReadExtensions,        scope: "analytics:read:extensions",         doc: "View analytics data for the Twitch Extensions owned by the authenticated account.";
    AnalyticsReadGames,             scope: "analytics:read:games",              doc: "View analytics data for the games owned by the authenticated account.";
    BitsRead,                       scope: "bits:read",                         doc: "View Bits information for a channel.";
    ChannelBot,                     scope: "channel:bot",                       doc: "Allows the client’s bot users access to a channel.";
    ChannelEditCommercial,          scope: "channel:edit:commercial",           doc: "Run commercials on a channel.";
    ChannelManageAds,               scope: "channel:manage:ads",                doc: "Manage ads schedule on a channel.";
    ChannelManageBroadcast,         scope: "channel:manage:broadcast",          doc: "Manage a channel’s broadcast configuration, including updating channel configuration and managing stream markers and stream tags.";
    ChannelManageExtensions,        scope: "channel:manage:extensions",         doc: "Manage a channel’s Extension configuration, including activating Extensions.";
    ChannelManageModerators,        scope: "channel:manage:moderators",         doc: "Add or remove the moderator role from users in your channel.";
    ChannelManagePolls,             scope: "channel:manage:polls",              doc: "Manage a channel’s polls.";
    ChannelManagePredictions,       scope: "channel:manage:predictions",        doc: "Manage of channel’s Channel Points Predictions";
    ChannelManageRaids,             scope: "channel:manage:raids",              doc: "Manage a channel raiding another channel.";
    ChannelManageRedemptions,       scope: "channel:manage:redemptions",        doc: "Manage Channel Points custom rewards and their redemptions on a channel.";
    ChannelManageSchedule,          scope: "channel:manage:schedule",           doc: "Manage a channel’s stream schedule.";
    ChannelManageVideos,            scope: "channel:manage:videos",             doc: "Manage a channel’s videos, including deleting videos.";
    ChannelManageVips,              scope: "channel:manage:vips",               doc: "Add or remove the VIP role from users in your channel.";
    ChannelModerate,                scope: "channel:moderate",                  doc: "Perform moderation actions in a channel. The user requesting the scope must be a moderator in the channel.";
    ChannelReadAds,                 scope: "channel:read:ads",                  doc: "Read the ads schedule and details on your channel.";
    ChannelReadCharity,             scope: "channel:read:charity",              doc: "Read charity campaign details and user donations on your channel.";
    ChannelReadEditors,             scope: "channel:read:editors",              doc: "View a list of users with the editor role for a channel.";
    ChannelReadGoals,               scope: "channel:read:goals",                doc: "View Creator Goals for a channel.";
    ChannelReadHypeTrain,           scope: "channel:read:hype_train",           doc: "View Hype Train information for a channel.";
    ChannelReadPolls,               scope: "channel:read:polls",                doc: "View a channel’s polls.";
    ChannelReadPredictions,         scope: "channel:read:predictions",          doc: "View a channel’s Channel Points Predictions.";
    ChannelReadRedemptions,         scope: "channel:read:redemptions",          doc: "View Channel Points custom rewards and their redemptions on a channel.";
    ChannelReadStreamKey,           scope: "channel:read:stream_key",           doc: "View an authorized user’s stream key.";
    ChannelReadSubscriptions,       scope: "channel:read:subscriptions",        doc: "View a list of all subscribers to a channel and check if a user is subscribed to a channel.";
    ChannelReadVips,                scope: "channel:read:vips",                 doc: "Read the list of VIPs in your channel.";
    #[deprecated(note = "Use `ChannelReadSubscriptions` (`channel:read:subscriptions`) instead")]
    ChannelSubscriptions,           scope: "channel_subscriptions",             doc: "Read all subscribers to your channel.";
    ChatEdit,                       scope: "chat:edit",                         doc: "Send live stream chat and rooms messages.";
    ChatRead,                       scope: "chat:read",                         doc: "View live stream chat and rooms messages.";
    ClipsEdit,                      scope: "clips:edit",                        doc: "Manage Clips for a channel.";
    ModerationRead,                 scope: "moderation:read",                   doc: "View a channel’s moderation data including Moderators, Bans, Timeouts, and Automod settings.";
    ModeratorManageAnnouncements,   scope: "moderator:manage:announcements",    doc: "Send announcements in channels where you have the moderator role.";
    ModeratorManageAutoMod,         scope: "moderator:manage:automod",          doc: "Manage messages held for review by AutoMod in channels where you are a moderator.";
    ModeratorManageAutomodSettings, scope: "moderator:manage:automod_settings", doc: "Manage a broadcaster’s AutoMod settings";
    ModeratorManageBannedUsers,     scope: "moderator:manage:banned_users",     doc: "Ban and unban users.";
    ModeratorManageBlockedTerms,    scope: "moderator:manage:blocked_terms",    doc: "Manage a broadcaster’s list of blocked terms.";
    ModeratorManageChatMessages,    scope: "moderator:manage:chat_messages",    doc: "Delete chat messages in channels where you have the moderator role";
    ModeratorManageChatSettings,    scope: "moderator:manage:chat_settings",    doc: "View a broadcaster’s chat room settings.";
    ModeratorManageShieldMode,      scope: "moderator:manage:shield_mode",      doc: "Manage a broadcaster’s Shield Mode status.";
    ModeratorManageShoutouts,       scope: "moderator:manage:shoutouts",        doc: "Manage a broadcaster’s shoutouts.";
    ModeratorReadAutomodSettings,   scope: "moderator:read:automod_settings",   doc: "View a broadcaster’s AutoMod settings.";
    ModeratorReadBlockedTerms,      scope: "moderator:read:blocked_terms",      doc: "View a broadcaster’s list of blocked terms.";
    ModeratorReadChatSettings,      scope: "moderator:read:chat_settings",      doc: "View a broadcaster’s chat room settings.";
    ModeratorReadChatters,          scope: "moderator:read:chatters",           doc: "View the chatters in a broadcaster’s chat room.";
    ModeratorReadFollowers,         scope: "moderator:read:followers",          doc: "Read the followers of a broadcaster.";
    ModeratorReadShieldMode,        scope: "moderator:read:shield_mode",        doc: "View a broadcaster’s Shield Mode status.";
    ModeratorReadShoutouts,         scope: "moderator:read:shoutouts",          doc: "View a broadcaster’s shoutouts.";
    UserBot,                        scope: "user:bot",                          doc: "Allows client’s bot to act as this user.";
    UserEdit,                       scope: "user:edit",                         doc: "Manage a user object.";
    UserEditBroadcast,              scope: "user:edit:broadcast",               doc: "Edit your channel's broadcast configuration, including extension configuration. (This scope implies user:read:broadcast capability.)";
    #[deprecated(note = "Not used anymore, see https://discuss.dev.twitch.tv/t/deprecation-of-create-and-delete-follows-api-endpoints/32351")]
    UserEditFollows,                scope: "user:edit:follows",                 doc: "\\[DEPRECATED\\] Was previously used for “Create User Follows” and “Delete User Follows.";
    UserManageBlockedUsers,         scope: "user:manage:blocked_users",         doc: "Manage the block list of a user.";
    UserManageChatColor,            scope: "user:manage:chat_color",            doc: "Update the color used for the user’s name in chat.Update User Chat Color";
    UserManageWhispers,             scope: "user:manage:whispers",              doc: "Read whispers that you send and receive, and send whispers on your behalf.";
    UserReadBlockedUsers,           scope: "user:read:blocked_users",           doc: "View the block list of a user.";
    UserReadBroadcast,              scope: "user:read:broadcast",               doc: "View a user’s broadcasting configuration, including Extension configurations.";
    UserReadChat,                   scope: "user:read:chat",                    doc: "View live stream chat and room messages.";
    UserReadEmail,                  scope: "user:read:email",                   doc: "View a user’s email address.";
    UserReadFollows,                scope: "user:read:follows",                 doc: "View the list of channels a user follows.";
    UserReadModeratedChannels,      scope: "user:read:moderated_channels",      doc: "Read the list of channels you have moderator privileges in.";
    UserReadSubscriptions,          scope: "user:read:subscriptions",           doc: "View if an authorized user is subscribed to specific channels.";
    UserWriteChat,                  scope: "user:write:chat",                   doc: "Send messages in a chat room.";
    WhispersEdit,                   scope: "whispers:edit",                     doc: "Send whisper messages.";
    WhispersRead,                   scope: "whispers:read",                     doc: "View your whisper messages.";
);

impl Scope {
    /// Get the scope as a [validator](Validator).
    pub const fn to_validator(self) -> Validator { Validator::scope(self) }
}

impl std::borrow::Borrow<str> for Scope {
    fn borrow(&self) -> &str { self.as_str() }
}

impl From<String> for Scope {
    fn from(s: String) -> Self { Scope::parse(s) }
}

impl From<Scope> for String {
    fn from(s: Scope) -> Self { s.to_string() }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn custom_scope() {
        assert_eq!(
            Scope::Other(Cow::from("custom_scope")),
            Scope::parse("custom_scope")
        )
    }

    #[test]
    fn roundabout() {
        for scope in Scope::all() {
            assert_eq!(scope, Scope::parse(scope.to_string()))
        }
    }

    #[test]
    #[allow(deprecated)]
    fn no_deprecated() {
        for scope in Scope::all() {
            assert!(scope != Scope::ChannelSubscriptions)
        }
    }
}