1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
#![doc(html_root_url = "https://docs.rs/twitter-client/0.0.1")] pub mod auth; pub mod error; pub mod request; pub mod response; pub mod traits; mod util; pub use crate::error::{Error, ErrorCode}; pub use crate::request::Request; pub use crate::response::Response; #[derive(Clone, Copy, Debug)] pub struct RateLimit { pub limit: u64, pub remaining: u64, pub reset: u64, } impl RateLimit { fn from_headers(headers: &http::HeaderMap) -> Option<Self> { pub const RATE_LIMIT_LIMIT: &str = "x-rate-limit-limit"; pub const RATE_LIMIT_REMAINING: &str = "x-rate-limit-remaining"; pub const RATE_LIMIT_RESET: &str = "x-rate-limit-reset"; fn header(headers: &http::HeaderMap, name: &str) -> Option<u64> { headers .get(name) .and_then(|value| atoi::atoi(value.as_bytes())) } Some(RateLimit { limit: header(headers, RATE_LIMIT_LIMIT)?, remaining: header(headers, RATE_LIMIT_REMAINING)?, reset: header(headers, RATE_LIMIT_RESET)?, }) } } #[cfg(test)] mod tests { use std::convert::Infallible; use hyper::Body; use oauth_credentials::Token; use tower::ServiceExt; use crate::request::RawRequest; use super::*; #[tokio::test] async fn parse_errors() { #[derive(oauth::Request)] struct Foo { param: u32, } impl RawRequest for Foo { fn method(&self) -> &http::Method { &http::Method::GET } fn uri(&self) -> &'static str { "https://api.twitter.com/1.1/test/foo.json" } } let token = Token::from_parts("", "", "", ""); let (http, mut handle) = tower_test::mock::pair::<http::Request<Vec<u8>>, _>(); let mut http = http.map_err(|e| -> Infallible { panic!("{:?}", e) }); let res_fut = tokio::spawn(Foo { param: 42 }.send_raw(&token, http.ready_and().await.unwrap())); let (_req, tx) = handle.next_request().await.unwrap(); let payload = br#"{"errors":[{"code":104,"message":"You aren't allowed to add members to this list."}]}"#; let res = http::Response::builder() .status(http::StatusCode::FORBIDDEN) .body(Body::from(&payload[..])) .unwrap(); tx.send_response(res); match res_fut.await.unwrap().unwrap_err() { Error::Twitter(crate::error::TwitterErrors { status: http::StatusCode::FORBIDDEN, errors, rate_limit: None, }) => { assert_eq!(errors.len(), 1); assert_eq!(errors[0].code, 104); assert_eq!( errors[0].message, "You aren't allowed to add members to this list.", ); } e => panic!("{:?}", e), }; } }