diff options
author | Kornel <kornel@geekhood.net> | 2020-04-11 12:47:21 +0100 |
---|---|---|
committer | Kornel <kornel@geekhood.net> | 2020-04-11 12:47:21 +0100 |
commit | f22c0ad0f9ba0fe2cad880a1ff94cbb3db31e252 (patch) | |
tree | c1a810e214fafe4a8b028ecaa33e254e32027c5e | |
parent | 4b1884b48efc673a2c36a88a45029ba1bd538148 (diff) |
Docs
-rw-r--r-- | github_v3/README.md | 16 | ||||
-rw-r--r-- | github_v3/src/lib.rs | 20 |
2 files changed, 35 insertions, 1 deletions
diff --git a/github_v3/README.md b/github_v3/README.md new file mode 100644 index 0000000..030a187 --- /dev/null +++ b/github_v3/README.md @@ -0,0 +1,16 @@ +# Async client for GitHub API v3 (`application/vnd.github.v3+json`) + +Written for [https://lib.rs](https://lib.rs). Supports only `get()` requests, because I didn't need more. [PR's welcome](https://gitlab.com/crates.rs/crates.rs/-/tree/master/github_v3). + +* Uses `async`/`await` and `std::futures`. + +* Supports streaming of GitHub's paged responses. + +* Automatically waits for responses that GitHub processes asynchronously in the background. + +* Automatically waits when hitting rate limit. + +* It's tiny, around 200 lines of code. + +It relies on [serde](https://lib.rs/serde) for parsing responses, so bring your own data model. + diff --git a/github_v3/src/lib.rs b/github_v3/src/lib.rs index b13fa96..a5fc31a 100644 --- a/github_v3/src/lib.rs +++ b/github_v3/src/lib.rs @@ -9,16 +9,19 @@ use std::sync::atomic::Ordering::SeqCst; use std::sync::Arc; use std::time::{Duration, SystemTime}; +/// Response from the API pub struct Response { res: reqwest::Response, client: Arc<ClientInner>, } impl Response { + /// Fetch a single JSON object from the API pub async fn obj<T: DeserializeOwned>(self) -> Result<T, GHError> { Ok(self.res.json().await?) } + /// Stream an array of objects from the API pub fn array<T: DeserializeOwned + std::marker::Unpin + 'static>(self) -> impl Stream<Item = Result<T, GHError>> { let mut res = self.res; let client = self.client; @@ -41,22 +44,30 @@ impl Response { }) } + /// Response headers pub fn headers(&self) -> &HeaderMap { self.res.headers() } + /// Response status pub fn status(&self) -> StatusCode { self.res.status() } } +/// See `Client::get()` +/// +/// Make a new request by constructing the request URL bit by bit pub struct Builder { client: Arc<ClientInner>, url: String, } impl Builder { - pub fn path(mut self, url_part: &str) -> Self { + /// Add a constant path to the request, e.g. `.path("users")` + /// + /// It's appended raw, so must be URL-safe. + pub fn path(mut self, url_part: &'static str) -> Self { debug_assert_eq!(url_part, url_part.trim_matches('/')); self.url.push('/'); @@ -73,6 +84,7 @@ impl Builder { self } + /// Make the request pub async fn send(self) -> Result<Response, GHError> { let res = self.client.raw_get(&self.url).await?; Ok(Response { @@ -88,15 +100,18 @@ struct ClientInner { wait_sec: AtomicU32, } +/// API Client. Start here. pub struct Client { inner: Arc<ClientInner>, } impl Client { + /// Reads `GITHUB_TOKEN` env var. pub fn new_from_env() -> Self { Self::new(std::env::var("GITHUB_TOKEN").ok().as_deref()) } + /// Takes API token for authenticated requests (make the token in GitHub settings) pub fn new(token: Option<&str>) -> Self { let mut default_headers = HeaderMap::with_capacity(2); default_headers.insert("Accept", HeaderValue::from_static("application/vnd.github.v3+json")); @@ -118,6 +133,7 @@ impl Client { } } + /// Make a new request to the API. pub fn get(&self) -> Builder { let mut url = String::with_capacity(60); url.push_str("https://api.github.com"); @@ -174,12 +190,14 @@ impl ClientInner { } } + /// GitHub's `x-ratelimit-remaining` header pub fn rate_limit_remaining(headers: &HeaderMap) -> Option<u32> { headers.get("x-ratelimit-remaining") .and_then(|s| s.to_str().ok()) .and_then(|s| s.parse().ok()) } + /// GitHub's `x-ratelimit-reset` header pub fn rate_limit_reset(headers: &HeaderMap) -> Option<SystemTime> { headers.get("x-ratelimit-reset") .and_then(|s| s.to_str().ok()) |