slack_web_api/
lib.rs

1pub mod api;
2mod builder;
3mod entity;
4mod error;
5
6pub use error::*;
7use std::io::Read;
8
9pub use builder::*;
10pub use entity::*;
11use hyper::http::request::Builder;
12use hyper::{Body, Method};
13use hyper_tls::HttpsConnector;
14use mpart_async::client::MultipartRequest;
15use mpart_async::filestream::FileStream;
16
17use serde_json::{json, Value};
18
19pub struct SlackClient {
20    pub(crate) context: SlackContext,
21}
22
23impl SlackClient {
24    pub fn new(token: &str) -> Self {
25        Self {
26            context: SlackContext {
27                token: Some(token.to_string()),
28            },
29        }
30    }
31}
32
33struct SlackContext {
34    token: Option<String>,
35}
36
37pub type SlackApiResponse<T> = Result<T, SlackError>;
38
39impl SlackClient {
40    async fn http_response<R>(response: hyper::Result<hyper::Response<Body>>) -> SlackApiResponse<R>
41    where
42        R: for<'de> serde::Deserialize<'de>,
43    {
44        let response = match response {
45            Ok(v) => v,
46            Err(e) => return Err(SlackSystemError::new(e.to_string()).into()),
47        };
48
49        let status = response.status();
50        let body = match hyper::body::to_bytes(response.into_body()).await {
51            Ok(v) => v.to_vec(),
52            Err(e) => {
53                return Err(SlackHttpError::new(status.as_u16(), e.message().to_string()).into());
54            }
55        };
56
57        let http_response_body = match String::from_utf8(body) {
58            Ok(v) => v,
59            Err(e) => return Err(SlackSystemError::new(e.to_string()).into()),
60        };
61
62        let value = match serde_json::from_str::<Value>(http_response_body.as_str()) {
63            Ok(v) => v,
64            Err(e) => return Err(SlackSystemError::new(e.to_string()).into()),
65        };
66        // dbg!(&value);
67        //
68        if let Some(ok) = value.get("ok") {
69            if ok.as_bool() == Some(false) {
70                return Err(SlackApiError {
71                    status: status.as_u16(),
72                    errors: Some(get_error_value(&value)),
73                    warnings: None,
74                    http_response_body: Some(http_response_body),
75                }
76                .into());
77            }
78        }
79
80        Ok(serde_json::from_value(value).unwrap())
81    }
82    pub(crate) async fn http_get<P, R>(
83        &self,
84        token: &str,
85        url: &str,
86        value: &P,
87    ) -> SlackApiResponse<R>
88    where
89        P: serde::Serialize,
90        R: for<'de> serde::Deserialize<'de>,
91    {
92        let build = builder(url, token).method(Method::GET);
93        let request = build.body(Body::from(json!(value).to_string())).unwrap();
94        let client = hyper::Client::builder().build(HttpsConnector::new());
95
96        let response = client.request(request).await;
97        SlackClient::http_response(response).await
98        // let body = hyper::body::to_bytes(response.into_body())
99        //     .await
100        //     .unwrap()
101        //     .to_vec();
102        // let body = String::from_utf8(body).unwrap();
103        // let value = serde_json::from_str::<Value>(body.as_str());
104        // dbg!(value);
105        //
106        // Err(SlackError::Http(SlackHttpError {
107        //     status: 0,
108        //     http_response_body: None,
109        // }))
110    }
111    pub(crate) async fn http_post<P, R>(&self, url: &str, value: &P) -> SlackApiResponse<R>
112    where
113        P: serde::Serialize,
114        R: for<'de> serde::Deserialize<'de>,
115    {
116        let build = builder(url, self.context.token.clone().unwrap_or_default().as_str())
117            .method(Method::POST);
118        // println!("hoge2 {}", serde_json::to_string(value).unwrap());
119        let request = build.body(Body::from(json!(value).to_string())).unwrap();
120        let response = hyper::Client::builder()
121            .build(HttpsConnector::new())
122            .request(request)
123            .await;
124        Self::http_response(response).await
125    }
126    pub(crate) async fn http_post_data<R>(
127        &self,
128        url: &str,
129        multipart: MultipartRequest<FileStream>,
130    ) -> SlackApiResponse<R>
131    where
132        R: for<'de> serde::Deserialize<'de>,
133    {
134        let mut build = builder_file(url, self.context.token.clone().unwrap_or_default().as_str())
135            .method(Method::POST);
136        build = build.header(
137            "Content-Type",
138            format!("multipart/form-data; boundary={}", multipart.get_boundary()),
139        );
140        let request = build.body(Body::wrap_stream(multipart)).unwrap();
141        let response = hyper::Client::builder()
142            .build(HttpsConnector::new())
143            .request(request)
144            .await;
145        Self::http_response(response).await
146    }
147}
148
149fn builder(url: &str, token: &str) -> Builder {
150    let mut build = hyper::Request::builder().uri(format!("https://slack.com/api/{}", url));
151    if !token.is_empty() {
152        build = build.header("Authorization", format!("Bearer {}", token))
153    }
154    build.header("Content-type", "application/json; charset=UTF-8")
155}
156
157fn builder_file(url: &str, token: &str) -> Builder {
158    let mut build = hyper::Request::builder().uri(format!("https://slack.com/api/{}", url));
159    if !token.is_empty() {
160        build = build.header("Authorization", format!("Bearer {}", token))
161    }
162    build
163    // build.header(CONTENT_TYPE, "multipart/form-data")
164    // build.header(CONTENT_TYPE, "application/x-www-form-urlencoded")
165}
166
167fn get_error_value(value: &Value) -> Vec<String> {
168    match value.get("error") {
169        None => {
170            vec![]
171        }
172        Some(v) => {
173            if v.is_string() {
174                return match v.as_str() {
175                    None => {
176                        vec![]
177                    }
178                    Some(v) => {
179                        vec![v.to_string()]
180                    }
181                };
182            }
183            vec![]
184        }
185    }
186}