reqwest_oauth1/
token_reader.rs

1use std::{collections::HashMap, future::Future};
2
3use async_trait::async_trait;
4use reqwest::Response;
5use serde::Deserialize;
6
7use crate::{Error, Result, TokenReaderError, TokenReaderResult};
8
9const OAUTH_TOKEN_KEY: &str = "oauth_token";
10
11const OAUTH_TOKEN_SECRET_KEY: &str = "oauth_token_secret";
12
13/// Represents response of token acquisition.
14#[derive(Deserialize, Debug)]
15pub struct TokenResponse {
16    /// OAuth Token
17    pub oauth_token: String,
18    /// OAuth Token Secret
19    pub oauth_token_secret: String,
20    /// Other contents
21    #[serde(flatten)]
22    pub remain: HashMap<String, String>,
23}
24
25/// Add parse_oauth_token feature to reqwest::Response.
26// this trait is sealed
27#[async_trait(?Send)]
28pub trait TokenReader: private::Sealed {
29    async fn parse_oauth_token(self) -> Result<TokenResponse>;
30}
31
32#[async_trait(?Send)]
33impl TokenReader for Response {
34    async fn parse_oauth_token(self) -> Result<TokenResponse> {
35        let text = self.text().await?;
36        // let text = self.error_for_status()?.text().await?;
37        // println!("{:#?}", text);
38        Ok(read_oauth_token(text)?)
39    }
40}
41
42/// Add parse_oauth_token feature to Future of reqwest::Response.
43// this trait is also sealed
44#[async_trait(?Send)]
45pub trait TokenReaderFuture: private::SealedWrapper {
46    async fn parse_oauth_token(self) -> Result<TokenResponse>;
47}
48
49/*
50#[async_trait(?Send)]
51impl<T> TokenReaderWrapper for T
52where
53    T: Future<Output = std::result::Result<Response, reqwest::Error>>,
54{
55    async fn parse_oauth_token(self) -> Result<TokenResponse> {
56        self.await?.parse_oauth_token().await
57    }
58}
59*/
60
61#[async_trait(?Send)]
62impl<T, E> TokenReaderFuture for T
63where
64    T: Future<Output = std::result::Result<Response, E>>,
65    E: Into<Error> + 'static,
66{
67    async fn parse_oauth_token(self) -> Result<TokenResponse> {
68        match self.await {
69            Ok(resp) => Ok(resp.parse_oauth_token().await?),
70            Err(err) => Err(err.into()),
71        }
72    }
73}
74
75fn read_oauth_token(text: String) -> TokenReaderResult<TokenResponse> { 
76    let mut destructured = text
77        .split("&")
78        .map(|e| e.splitn(2, "="))
79        .map(|v| {
80            let mut iter = v.into_iter();
81            (
82                iter.next().unwrap_or_default().to_string(),
83                iter.next().unwrap_or_default().to_string(),
84            )
85        })
86        .collect::<HashMap<String, String>>();
87    let oauth_token = destructured.remove(OAUTH_TOKEN_KEY);
88    let oauth_token_secret = destructured.remove(OAUTH_TOKEN_SECRET_KEY);
89    match (oauth_token, oauth_token_secret) {
90        (Some(t), Some(s)) => Ok(TokenResponse {
91            oauth_token: t,
92            oauth_token_secret: s,
93            remain: destructured,
94        }),
95        (None, _) => Err(TokenReaderError::TokenKeyNotFound(OAUTH_TOKEN_KEY, text)),
96        (_, _) => Err(TokenReaderError::TokenKeyNotFound(
97            OAUTH_TOKEN_SECRET_KEY,
98            text,
99        )),
100    }
101}
102
103mod private {
104    use std::future::Future;
105
106    use reqwest::Response;
107
108    use crate::Error;
109
110    pub trait Sealed {}
111    impl Sealed for Response {}
112    pub trait SealedWrapper {}
113    // impl<T> SealedWrapper for T where T: Future<Output = reqwest::Result<Response>> {}
114    impl<T, E> SealedWrapper for T
115    where
116        T: Future<Output = Result<Response, E>>,
117        E: Into<Error>,
118    {
119    }
120}
121
122#[cfg(test)]
123mod test {
124
125    use super::*;
126
127    #[test]
128    fn parse_response_typical() {
129        let resp_str_sample = "oauth_token=Z6eEdO8MOmk394WozF5oKyuAv855l4Mlqo7hhlSLik&oauth_token_secret=Kd75W4OQfb2oJTV0vzGzeXftVAwgMnEK9MumzYcM&oauth_callback_confirmed=true";
130        for parsed in &[
131            read_oauth_token(resp_str_sample.to_string()).unwrap(),
132            serde_urlencoded::from_str::<TokenResponse>(resp_str_sample).unwrap(),
133        ] {
134            assert_eq!(
135                parsed.oauth_token,
136                "Z6eEdO8MOmk394WozF5oKyuAv855l4Mlqo7hhlSLik"
137            );
138            assert_eq!(
139                parsed.oauth_token_secret,
140                "Kd75W4OQfb2oJTV0vzGzeXftVAwgMnEK9MumzYcM"
141            );
142            assert_eq!(parsed.remain.len(), 1);
143            let oauth_callback_confirmed = parsed.remain.get("oauth_callback_confirmed").unwrap();
144            assert_eq!(oauth_callback_confirmed, "true");
145        }
146    }
147
148    #[test]
149    fn parse_response_edge() {
150        let resp_str_sample = "oauth_token==&oauth_token_secret=&keyonly=&keyonly2&=&&";
151        for parsed in &[
152            read_oauth_token(resp_str_sample.to_string()).unwrap(),
153            serde_urlencoded::from_str::<TokenResponse>(resp_str_sample).unwrap(),
154        ] {
155            assert_eq!(parsed.oauth_token, "=");
156            assert_eq!(parsed.oauth_token_secret, "");
157            assert_eq!(parsed.remain.len(), 3);
158            let keyonly = parsed.remain.get("keyonly").unwrap();
159            assert_eq!(keyonly, "");
160            let keyonly2 = parsed.remain.get("keyonly2").unwrap();
161            assert_eq!(keyonly2, "");
162            let empty = parsed.remain.get("").unwrap();
163            assert_eq!(empty, "");
164        }
165    }
166
167    #[test]
168    fn parse_minimal() {
169        let resp_str_sample = "oauth_token&oauth_token_secret";
170        let parsed = read_oauth_token(resp_str_sample.to_string()).unwrap();
171        assert_eq!(parsed.oauth_token, "");
172        assert_eq!(parsed.oauth_token_secret, "");
173        assert_eq!(parsed.remain.len(), 0);
174    }
175
176    #[test]
177    fn parse_token_notfound() {
178        let resp_str_sample = "oauth_token_secret=";
179        let parsed = read_oauth_token(resp_str_sample.to_string());
180        assert!(parsed.is_err());
181        if let Err(TokenReaderError::TokenKeyNotFound(key, resp_str)) = parsed {
182            assert_eq!(key, OAUTH_TOKEN_KEY);
183            assert_eq!(resp_str, resp_str_sample)
184        } else {
185            assert!(false)
186        }
187    }
188
189    #[test]
190    fn parse_token_secret_notfound() {
191        let resp_str_sample = "oauth_token=";
192        let parsed = read_oauth_token(resp_str_sample.to_string());
193        assert!(parsed.is_err());
194        if let Err(TokenReaderError::TokenKeyNotFound(key, resp_str)) = parsed {
195            assert_eq!(key, OAUTH_TOKEN_SECRET_KEY);
196            assert_eq!(resp_str, resp_str_sample)
197        } else {
198            assert!(false)
199        }
200    }
201}