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
//! Module for all possible scopes in twitch.

use serde::{Deserialize, Serialize};

macro_rules! scope_impls {
    ($($i:ident,$rename:literal,$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 {
            $(
                #[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(String),
        }

        impl std::fmt::Display for Scope {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                f.write_str(match self {
                    Scope::Other(s) => s.as_str(),
                    $(
                        Scope::$i => $rename,
                    )*
                })
            }
        }

        impl Scope {
            #[doc = "Get a vec of all defined twitch [Scopes][Scope]."]
            #[doc = "\n\n"]
            #[doc = "Please not 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::$i,)*
                ]
            }

            #[doc = "Make a scope from string"]
            pub fn parse(s: &str) -> Scope {
                match s {
                    $($rename => {Scope::$i})*,
                    other => Scope::Other(other.to_string())
                }
            }
        }

    };
}

scope_impls!(
    AnalyticsReadExtensions, "analytics:read:extensions", "View analytics data for your extensions.";
    AnalyticsReadGames, "analytics:read:games", "View analytics data for your games.";
    BitsRead, "bits:read", "View bits information for your channel.";
    ChannelSubscriptions, "channel_subscriptions", "\\[DEPRECATED\\] Read all subscribers to your channel.";
    ChannelEditCommercial, "channel:edit:commercial", "Start a commercial on authorized channels";
    ChannelManageBroadcast, "channel:manage:broadcast", "Manage your channel’s broadcast configuration, including updating channel configuration and managing stream markers and stream tags.";
    ChannelManageExtension, "channel:manage:extension", "Manage your channel’s extension configuration, including activating extensions.";
    ChannelModerate, "channel:moderate", "Perform moderation actions in a channel";
    ChannelReadHypeTrain, "channel:read:hype_train", "Read hype trains";
    ChannelReadRedemptions, "channel:read:redemptions", "View your channel points custom reward redemptions";
    ChannelReadSubscriptions, "channel:read:subscriptions", "Get a list of all subscribers to your channel and check if a user is subscribed to your channel";
    ChatEdit, "chat:edit", "Send live Stream Chat and Rooms messages";
    ChatRead, "chat:read", "View live Stream Chat and Rooms messages";
    ClipsEdit, "clips:edit", "Create and edit clips as a specific user.";
    ModerationRead, "moderation:read", "View your channel's moderation data including Moderators, Bans, Timeouts and Automod settings";
    UserEdit, "user:edit", "Manage a user object.";
    UserEditBroadcast, "user:edit:broadcast", "Edit your channel's broadcast configuration, including extension configuration. (This scope implies user:read:broadcast capability.)";
    UserEditFollows, "user:edit:follows", "Edit your follows.";
    UserReadBroadcast, "user:read:broadcast", "View your broadcasting configuration, including extension configurations.";
    UserReadEmail, "user:read:email", "Read authorized user's email address.";
    UserReadStreamKey, "user:read:stream_key", "Read authorized user’s stream key.";
    WhispersEdit, "whispers:edit", "Send whisper messages.";
    WhispersRead, "whispers:read", "View your whisper messages.";
);

impl Scope {
    /// Get [Scope] as an oauth2 Scope
    pub fn as_oauth_scope(&self) -> oauth2::Scope { oauth2::Scope::new(self.to_string()) }
}

impl From<oauth2::Scope> for Scope {
    fn from(scope: oauth2::Scope) -> Self { Scope::parse(scope.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(String::from("custom_scope")),
            Scope::parse("custom_scope")
        )
    }

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