Skip to main content

discogs_rs/
auth.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
4pub enum AuthLevel {
5    None,
6    Consumer,
7    User,
8}
9
10#[derive(Debug, Clone, Default)]
11pub enum Auth {
12    #[default]
13    None,
14    UserToken {
15        token: String,
16    },
17    Discogs {
18        consumer_key: String,
19        consumer_secret: String,
20    },
21    OAuth {
22        consumer_key: String,
23        consumer_secret: String,
24        access_token: String,
25        access_token_secret: String,
26    },
27}
28
29impl Auth {
30    pub fn level(&self) -> AuthLevel {
31        match self {
32            Auth::None => AuthLevel::None,
33            Auth::Discogs { .. } => AuthLevel::Consumer,
34            Auth::UserToken { .. } | Auth::OAuth { .. } => AuthLevel::User,
35        }
36    }
37
38    pub fn authorization_header(&self) -> Option<String> {
39        match self {
40            Auth::None => None,
41            Auth::UserToken { token } => Some(format!("Discogs token={token}")),
42            Auth::Discogs {
43                consumer_key,
44                consumer_secret,
45            } => Some(format!(
46                "Discogs key={consumer_key}, secret={consumer_secret}"
47            )),
48            Auth::OAuth {
49                consumer_key,
50                consumer_secret,
51                access_token,
52                access_token_secret,
53            } => Some(crate::oauth::build_oauth_header(
54                consumer_key,
55                consumer_secret,
56                access_token,
57                access_token_secret,
58            )),
59        }
60    }
61}
62
63#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
64#[serde(rename_all = "snake_case")]
65pub enum OutputFormat {
66    #[default]
67    Discogs,
68    Plaintext,
69    Html,
70}
71
72impl OutputFormat {
73    pub fn accept_header_value(self) -> &'static str {
74        match self {
75            OutputFormat::Discogs => "application/vnd.discogs.v2.discogs+json",
76            OutputFormat::Plaintext => "application/vnd.discogs.v2.plaintext+json",
77            OutputFormat::Html => "application/vnd.discogs.v2.html+json",
78        }
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::{Auth, AuthLevel, OutputFormat};
85
86    #[test]
87    fn auth_levels_match_expected_capabilities() {
88        assert_eq!(Auth::None.level(), AuthLevel::None);
89        assert_eq!(
90            Auth::Discogs {
91                consumer_key: "k".into(),
92                consumer_secret: "s".into(),
93            }
94            .level(),
95            AuthLevel::Consumer
96        );
97        assert_eq!(
98            Auth::UserToken { token: "t".into() }.level(),
99            AuthLevel::User
100        );
101    }
102
103    #[test]
104    fn output_accept_header_is_stable() {
105        assert_eq!(
106            OutputFormat::Discogs.accept_header_value(),
107            "application/vnd.discogs.v2.discogs+json"
108        );
109        assert_eq!(
110            OutputFormat::Plaintext.accept_header_value(),
111            "application/vnd.discogs.v2.plaintext+json"
112        );
113        assert_eq!(
114            OutputFormat::Html.accept_header_value(),
115            "application/vnd.discogs.v2.html+json"
116        );
117    }
118
119    #[test]
120    fn user_token_auth_header_format_is_stable() {
121        let header = Auth::UserToken {
122            token: "abc123".into(),
123        }
124        .authorization_header()
125        .expect("header should exist");
126
127        assert_eq!(header, "Discogs token=abc123");
128    }
129}