summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Blajda <blajda@hotmail.com>2018-12-16 19:31:45 +0000
committerDavid Blajda <blajda@hotmail.com>2018-12-16 19:31:45 +0000
commitbe68a7da226743edbce5b52e506d9083e2859578 (patch)
treebd0a04bf63637d2392495c06d65bdc242be15f6b
parent136f56e2d9a010ea76041ba6b44873491ddef848 (diff)
Prototype of a trait based client
-rw-r--r--src/bin/main.rs13
-rw-r--r--src/helix/endpoints.rs68
-rw-r--r--src/helix/mod.rs146
-rw-r--r--src/lib.rs28
-rw-r--r--src/types.rs4
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,
-}
/*
diff --git a/src/lib.rs b/src/lib.rs
index e0100fa..21f0cb9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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 */
/*