oauth2_twitter/extensions/
internal_users_me_endpoint.rs

1// [Users lookup](https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users-me)
2
3use oauth2_client::re_exports::{
4    http::header::{ACCEPT, AUTHORIZATION},
5    serde_json, thiserror, Body, Deserialize, Endpoint, HttpError, Request, Response,
6    SerdeJsonError, Serialize, Url, UrlParseError, MIME_APPLICATION_JSON,
7};
8
9pub const URL: &str = "https://api.twitter.com/2/users/me";
10
11//
12#[derive(Debug, Clone)]
13pub struct UsersMeEndpoint {
14    access_token: String,
15}
16impl UsersMeEndpoint {
17    pub fn new(access_token: impl AsRef<str>) -> Self {
18        Self {
19            access_token: access_token.as_ref().to_owned(),
20        }
21    }
22}
23
24impl Endpoint for UsersMeEndpoint {
25    type RenderRequestError = UsersMeEndpointError;
26
27    type ParseResponseOutput = User;
28    type ParseResponseError = UsersMeEndpointError;
29
30    fn render_request(&self) -> Result<Request<Body>, Self::RenderRequestError> {
31        let mut url = Url::parse(URL).map_err(UsersMeEndpointError::UrlParseFailed)?;
32        url.query_pairs_mut()
33            .append_pair("user.fields", "id,name,username,created_at,description");
34
35        let request = Request::builder()
36            .uri(url.as_str())
37            .header(AUTHORIZATION, format!("Bearer {}", &self.access_token))
38            .header(ACCEPT, MIME_APPLICATION_JSON)
39            .body(vec![])
40            .map_err(UsersMeEndpointError::MakeRequestFailed)?;
41
42        Ok(request)
43    }
44
45    fn parse_response(
46        &self,
47        response: Response<Body>,
48    ) -> Result<Self::ParseResponseOutput, Self::ParseResponseError> {
49        if response.status().is_success() {
50            let body = serde_json::from_slice::<ResponseOkBody>(response.body())
51                .map_err(UsersMeEndpointError::DeResponseBodyFailed)?;
52
53            Ok(body.data)
54        } else {
55            let body = serde_json::from_slice::<ResponseFailBody>(response.body())
56                .map_err(UsersMeEndpointError::DeResponseBodyFailed)?;
57
58            Err(UsersMeEndpointError::ResponseBodyIsFail(body))
59        }
60    }
61}
62
63#[derive(Deserialize, Serialize, Debug, Clone)]
64pub struct ResponseOkBody {
65    pub data: User,
66}
67
68#[derive(Deserialize, Serialize, Debug, Clone)]
69pub struct User {
70    pub id: String,
71    pub name: String,
72    pub username: String,
73    pub created_at: Option<String>,
74    pub description: Option<String>,
75}
76
77#[derive(Deserialize, Serialize, Debug, Clone)]
78pub struct ResponseFailBody {
79    pub title: String,
80    pub r#type: String,
81    pub status: usize,
82    pub detail: String,
83}
84
85impl core::fmt::Display for ResponseFailBody {
86    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
87        write!(f, "{self:?}")
88    }
89}
90impl std::error::Error for ResponseFailBody {}
91
92#[derive(thiserror::Error, Debug)]
93pub enum UsersMeEndpointError {
94    #[error("UrlParseFailed {0}")]
95    UrlParseFailed(UrlParseError),
96    //
97    #[error("MakeRequestFailed {0}")]
98    MakeRequestFailed(HttpError),
99    //
100    #[error("DeResponseBodyFailed {0}")]
101    DeResponseBodyFailed(SerdeJsonError),
102    //
103    #[error("ResponseBodyIsFail {0:?}")]
104    ResponseBodyIsFail(ResponseFailBody),
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn de_response_body() {
113        match serde_json::from_str::<ResponseOkBody>(include_str!(
114            "../../tests/response_body_json_files/users_me.json"
115        )) {
116            Ok(body) => {
117                assert_eq!(body.data.name, "HAWE");
118            }
119            Err(err) => panic!("{err}"),
120        }
121
122        match serde_json::from_str::<ResponseFailBody>(include_str!(
123            "../../tests/response_body_json_files/users_me_fail.json"
124        )) {
125            Ok(_) => {}
126            Err(err) => panic!("{err}"),
127        }
128    }
129}