diff options
author | David Blajda <blajda@hotmail.com> | 2018-12-16 19:31:45 +0000 |
---|---|---|
committer | David Blajda <blajda@hotmail.com> | 2018-12-16 19:31:45 +0000 |
commit | be68a7da226743edbce5b52e506d9083e2859578 (patch) | |
tree | bd0a04bf63637d2392495c06d65bdc242be15f6b | |
parent | 136f56e2d9a010ea76041ba6b44873491ddef848 (diff) |
Prototype of a trait based client
-rw-r--r-- | src/bin/main.rs | 13 | ||||
-rw-r--r-- | src/helix/endpoints.rs | 68 | ||||
-rw-r--r-- | src/helix/mod.rs | 146 | ||||
-rw-r--r-- | src/lib.rs | 28 | ||||
-rw-r--r-- | src/types.rs | 4 |
5 files changed, 222 insertions, 37 deletions
diff --git a/src/bin/main.rs b/src/bin/main.rs index 7fdde47..45b25df 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -8,12 +8,15 @@ use futures::future::Future; use std::env; use twitch_api::Client; +use twitch_api::helix::HelixClient; + fn main() { dotenv::dotenv().unwrap(); let client_id = &env::var("TWITCH_API").unwrap(); let client = Client::new(client_id); - let clip = client.helix + + let clip = client.helix.clips() .clip(&"EnergeticApatheticTarsierThisIsSparta") .map_err(|err| { println!("{:?}", err); @@ -27,13 +30,19 @@ fn main() { () }); + /* Prevents tokio from **hanging** + * since tokio::run blocks the current thread and waits for the entire runtime + * to become idle but it will never becomes idle since we keep a reference + * to a reqwest client which maintains a connection pool. + */ + std::mem::drop(client); tokio::run( clip.join(clip2) .and_then(|(c1, c2)| { println!("{:?} {:?}", c1, c2); Ok((c1, c2)) }) - .map(|_| { client.nop(); ()}) + .map(|_| ()) .map_err(|_| ()) ); } diff --git a/src/helix/endpoints.rs b/src/helix/endpoints.rs index bb61ae9..80b4ebb 100644 --- a/src/helix/endpoints.rs +++ b/src/helix/endpoints.rs @@ -2,6 +2,7 @@ use futures::future::Future; use reqwest::header; use reqwest::r#async::{RequestBuilder}; use reqwest::r#async::Client as ReqwestClient; +use std::sync::Arc; use super::models::{DataContainer, PaginationContainer, User, Video, Clip}; use super::Client; @@ -13,15 +14,51 @@ const API_DOMAIN: &'static str = "api.twitch.tv"; * connections. */ -impl Client { +use super::super::GetRequest; +use super::super::GetRequestRef; +use std::marker::PhantomData; + +use super::HelixClient; - fn apply_standard_headers(&self, request: RequestBuilder) - -> RequestBuilder - { - let client_header = header::HeaderValue::from_str(&self.inner.id).unwrap(); +fn apply_standard_headers(client: &Box<dyn HelixClient>, request: RequestBuilder) + -> RequestBuilder +{ + let client_header = header::HeaderValue::from_str(client.id()).unwrap(); + + request.header("Client-ID", client_header) +} - request.header("Client-ID", client_header) +pub fn clip(client: &Box<dyn HelixClient>, id: &str) + -> impl Future<Item=DataContainer<Clip>, Error=reqwest::Error> +{ + let url = + String::from("https://") + + API_DOMAIN + "/helix/clips" + "?id=" + id; + + + let request = client.client().get(&url); + let request = apply_standard_headers(client, request); + + request + .send() + .map(|mut res| { + println!("{:?}", res); + res.json::<DataContainer<Clip>>() + }) + .and_then(|json| json) + /* + GetRequest { + inner: Arc::new(GetRequestRef { + url: url.to_owned(), + returns: PhantomData, + }) } + */ +} + +impl Client { + + /* pub fn users( @@ -90,22 +127,5 @@ impl Client { } */ - pub fn clip(&self, id: &str) - -> impl Future<Item=DataContainer<Clip>, Error=reqwest::Error> - { - let url = - String::from("https://") + - API_DOMAIN + "/helix/clips" + "?id=" + id; - - let request = self.inner.client.get(&url); - let request = self.apply_standard_headers(request); - - request - .send() - .map(|mut res| { - println!("{:?}", res); - res.json::<DataContainer<Clip>>() - }) - .and_then(|json| json) - } } + diff --git a/src/helix/mod.rs b/src/helix/mod.rs index d8c9952..50412fb 100644 --- a/src/helix/mod.rs +++ b/src/helix/mod.rs @@ -1,15 +1,155 @@ -use std::sync::Arc; +use futures::future::Future; +use std::sync::{Arc, Mutex}; use reqwest::r#async::Client as ReqwestClient; pub use super::types; pub mod endpoints; pub mod models; +use std::collections::HashSet; + #[derive(Clone)] pub struct Client { inner: Arc<ClientRef> } +struct ClientRef { + id: String, + client: ReqwestClient, +} + +use self::models::{DataContainer, PaginationContainer, Clip}; + +pub trait UsersEndpoint {} +pub trait VideosEndpoint {} + +pub trait ClipsEndpointTrait { + fn clip(&self, id: &str) -> Box<Future<Item=DataContainer<Clip>, Error=reqwest::Error> + Send>; +} + +pub trait AuthEndpoint {} + +pub struct ClipsEndpoint { + client: Box<dyn HelixClient> +} + +impl ClipsEndpointTrait for ClipsEndpoint { + fn clip(&self, id: &str) -> Box<Future<Item=DataContainer<Clip>, Error=reqwest::Error> + Send> { + use self::endpoints::clip; + Box::new(clip(&self.client, id)) + } + +} + +pub trait HelixClient { + //fn users(&self) -> Box<dyn UsersEndpoint>; + //fn videos(&self) -> Box<dyn VideosEndpoint>; + fn clips(&self) -> Box<dyn ClipsEndpointTrait>; + //fn auth(&self) -> Box<dyn AuthEndpoint>; + + fn id(&self) -> &str; + fn authenticated(&self) -> bool; + fn with_auth(self, secret: &str) -> AuthClientBuilder; + fn unauth(self) -> Box<dyn HelixClient>; + + /* I don't want to expose this. Temp work around*/ + fn client(&self) -> &ReqwestClient; +} + +impl HelixClient for Client { + + fn clips(&self) -> Box<dyn ClipsEndpointTrait> { + Box::new(ClipsEndpoint { + client: Box::new(self.clone()) + }) + } + + fn id(&self) -> &str { + &self.inner.id + } + + fn authenticated(&self) -> bool { + false + } + + fn with_auth(self, secret: &str) -> AuthClientBuilder { + AuthClientBuilder::new(Box::new(self), secret) + } + + fn unauth(self) -> Box<dyn HelixClient> { + Box::new(self) + } + + fn client(&self) -> &ReqwestClient { + &self.inner.client + } +} + + +#[derive(Clone)] +pub struct AuthClient { + +} + +pub struct AuthClientBuilder { + scopes: HashSet<Scope>, + secret: String, + client: Box<HelixClient>, + /*If the user supplies a token, + * then we can skip fetching it from the server and are authenticated + */ + token: Option<String>, +} + +impl AuthClientBuilder { + pub fn new(client: Box<dyn HelixClient>, secret: &str) -> AuthClientBuilder { + AuthClientBuilder { + scopes: HashSet::new(), + secret: secret.to_owned(), + client: client, + token: None, + } + } + + /* + pub fn build(self) -> Box<dyn HelixClient> { + AuthClient { + } + } + */ + + pub fn scope(mut self, scope: Scope) -> AuthClientBuilder { + let scopes = &mut self.scopes; + scopes.insert(scope); + self + } + + pub fn scopes(mut self, scopes: Vec<Scope>) -> AuthClientBuilder { + let _scopes = &mut self.scopes; + for scope in scopes { + _scopes.insert(scope); + } + self + } + + pub fn token(self, token: &str) -> AuthClientBuilder { + self + } +} + + +#[derive(PartialEq, Hash, Eq)] +pub enum Scope { + AnalyticsReadExtensions, + AnalyticsReadGames, + BitsRead, + ClipsEdit, + UserEdit, + UserEditBroadcast, + UserReadBroadcast, + UserReadEmail, +} + impl Client { pub fn new(id: &str) -> Client { Client { @@ -31,10 +171,6 @@ impl Client { } } -struct ClientRef { - id: String, - client: ReqwestClient, -} /* @@ -2,9 +2,7 @@ extern crate futures; extern crate reqwest; extern crate serde; extern crate chrono; - -#[macro_use] -extern crate serde_derive; +#[macro_use] extern crate serde_derive; pub mod helix; pub mod kraken; @@ -12,7 +10,12 @@ pub mod types; pub use self::helix::Client as HelixClient; pub use self::kraken::Client as KrakenClient; + use reqwest::r#async::Client as ReqwestClient; +use reqwest::header::HeaderMap; +use std::marker::PhantomData; +use std::sync::Arc; +use std::collections::BTreeMap; pub struct Client { pub helix: HelixClient, @@ -27,7 +30,22 @@ impl Client { kraken: KrakenClient::new_with_client(client_id, client.clone()), } } +} - pub fn nop(self) { - } +trait Request<T> { + fn url(&self) -> String; + fn headers(&self) -> &HeaderMap; + fn query(&self) -> &BTreeMap<String, String>; + fn returns(&self) -> T; +} + +pub struct GetRequest<T> { + inner: Arc<GetRequestRef<T>>, +} + +struct GetRequestRef<T> { + url: String, +// headers: HeaderMap, +// query: BTreeMap<String, String>, + returns: PhantomData<T>, } diff --git a/src/types.rs b/src/types.rs index f3ba5bd..09f2063 100644 --- a/src/types.rs +++ b/src/types.rs @@ -133,16 +133,18 @@ mod tests { assert_eq!(u1.is_ok(), true); assert_eq!(u2.is_ok(), true); + let u1 = u1.unwrap(); let u2 = u2.unwrap(); - assert_eq!(u1, u2); assert_eq!(u1, 1234); assert_eq!(u1, "1234"); let u2 = UserId::from_str("1235").unwrap(); assert_ne!(u1, u2); + assert_ne!(u1, 1235); + assert_ne!(u1, "1235"); /* This must give a compile error */ /* |