pinboard_rs/api/
ignore.rs

1// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4// option. This file may not be copied, modified, or distributed
5// except according to those terms.
6
7use async_trait::async_trait;
8use http::{header, Request};
9
10use crate::api::{query, ApiError, AsyncClient, AsyncQuery, Client, Endpoint, Query};
11
12/// A query modifier that ignores the data returned from an endpoint.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct Ignore<E> {
15    endpoint: E,
16}
17
18/// Ignore the resulting data from an endpoint.
19pub fn ignore<E>(endpoint: E) -> Ignore<E> {
20    Ignore { endpoint }
21}
22
23impl<E, C> Query<(), C> for Ignore<E>
24where
25    E: Endpoint,
26    C: Client,
27{
28    fn query(&self, client: &C) -> Result<(), ApiError<C::Error>> {
29        let mut url = client.rest_endpoint(&self.endpoint.endpoint())?;
30        self.endpoint.parameters().add_to_url(&mut url);
31
32        let req = Request::builder()
33            .method(self.endpoint.method())
34            .uri(query::url_to_http_uri(url));
35        let (req, data) = if let Some((mime, data)) = self.endpoint.body()? {
36            let req = req.header(header::CONTENT_TYPE, mime);
37            (req, data)
38        } else {
39            (req, Vec::new())
40        };
41        let rsp = client.rest(req, data)?;
42        if !rsp.status().is_success() {
43            let v = serde_json::from_slice(rsp.body())?;
44            return Err(ApiError::from_pinboard(v));
45        }
46
47        Ok(())
48    }
49}
50
51#[async_trait]
52impl<E, C> AsyncQuery<(), C> for Ignore<E>
53where
54    E: Endpoint + Sync,
55    C: AsyncClient + Sync,
56{
57    async fn query_async(&self, client: &C) -> Result<(), ApiError<C::Error>> {
58        let mut url = client.rest_endpoint(&self.endpoint.endpoint())?;
59        self.endpoint.parameters().add_to_url(&mut url);
60
61        let req = Request::builder()
62            .method(self.endpoint.method())
63            .uri(query::url_to_http_uri(url));
64        let (req, data) = if let Some((mime, data)) = self.endpoint.body()? {
65            let req = req.header(header::CONTENT_TYPE, mime);
66            (req, data)
67        } else {
68            (req, Vec::new())
69        };
70        let rsp = client.rest_async(req, data).await?;
71        if !rsp.status().is_success() {
72            let v = serde_json::from_slice(rsp.body())?;
73            return Err(ApiError::from_pinboard(v));
74        }
75
76        Ok(())
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use http::StatusCode;
83    use serde_json::json;
84
85    use crate::api::endpoint_prelude::*;
86    use crate::api::{self, ApiError, AsyncQuery, Query};
87    use crate::test::client::{ExpectedUrl, SingleTestClient};
88
89    struct Dummy;
90
91    impl Endpoint for Dummy {
92        fn method(&self) -> Method {
93            Method::GET
94        }
95
96        fn endpoint(&self) -> Cow<'static, str> {
97            "dummy".into()
98        }
99    }
100
101    #[test]
102    fn test_pinboard_non_json_response() {
103        let endpoint = ExpectedUrl::builder().endpoint("dummy").build().unwrap();
104        let client = SingleTestClient::new_raw(endpoint, "not json");
105
106        api::ignore(Dummy).query(&client).unwrap()
107    }
108
109    #[tokio::test]
110    async fn test_pinboard_non_json_response_async() {
111        let endpoint = ExpectedUrl::builder().endpoint("dummy").build().unwrap();
112        let client = SingleTestClient::new_raw(endpoint, "not json");
113
114        api::ignore(Dummy).query_async(&client).await.unwrap()
115    }
116
117    #[test]
118    fn test_pinboard_error_bad_json() {
119        let endpoint = ExpectedUrl::builder()
120            .endpoint("dummy")
121            .status(StatusCode::NOT_FOUND)
122            .build()
123            .unwrap();
124        let client = SingleTestClient::new_raw(endpoint, "");
125
126        let err = api::ignore(Dummy).query(&client).unwrap_err();
127        if let ApiError::Json { source } = err {
128            assert_eq!(
129                format!("{}", source),
130                "EOF while parsing a value at line 1 column 0",
131            );
132        } else {
133            panic!("unexpected error: {}", err);
134        }
135    }
136
137    #[test]
138    fn test_pinboard_error_detection() {
139        let endpoint = ExpectedUrl::builder()
140            .endpoint("dummy")
141            .status(StatusCode::NOT_FOUND)
142            .build()
143            .unwrap();
144        let client = SingleTestClient::new_json(
145            endpoint,
146            &json!({
147                "message": "dummy error message",
148            }),
149        );
150
151        let err = api::ignore(Dummy).query(&client).unwrap_err();
152        if let ApiError::PinboardUnrecognized { obj } = err {
153            assert_eq!(
154                obj.pointer("/message").expect("error message"),
155                "dummy error message"
156            );
157        } else {
158            panic!("unexpected error: {}", err);
159        }
160    }
161
162    #[test]
163    fn test_pinboard_error_detection_legacy() {
164        let endpoint = ExpectedUrl::builder()
165            .endpoint("dummy")
166            .status(StatusCode::NOT_FOUND)
167            .build()
168            .unwrap();
169        let client = SingleTestClient::new_json(
170            endpoint,
171            &json!({
172                "error": "dummy error message",
173            }),
174        );
175
176        let err = api::ignore(Dummy).query(&client).unwrap_err();
177        println!("ERR: <{:?}>", err);
178        if let ApiError::PinboardUnrecognized { obj } = err {
179            assert_eq!(
180                obj.pointer("/error").expect("error message"),
181                "dummy error message"
182            );
183        } else {
184            panic!("unexpected error: {}", err);
185        }
186    }
187
188    #[test]
189    fn test_pinboard_error_detection_unknown() {
190        let endpoint = ExpectedUrl::builder()
191            .endpoint("dummy")
192            .status(StatusCode::NOT_FOUND)
193            .build()
194            .unwrap();
195        let err_obj = json!({
196            "bogus": "dummy error message",
197        });
198        let client = SingleTestClient::new_json(endpoint, &err_obj);
199
200        let err = api::ignore(Dummy).query(&client).unwrap_err();
201        if let ApiError::PinboardUnrecognized { obj } = err {
202            assert_eq!(obj, err_obj);
203        } else {
204            panic!("unexpected error: {}", err);
205        }
206    }
207}