summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDavid Blajda <blajda@hotmail.com>2019-05-08 15:38:37 +0000
committerDavid Blajda <blajda@hotmail.com>2019-05-08 15:38:37 +0000
commit98fb79b85e3cfbb547e5340b30623511daf09ef5 (patch)
treea03bd7a95c86a92eb7510409429f50433056d7ab /src
parent0e09c2c06fc0b81f11d12985a4172c815233db17 (diff)
:WIP: Move types to a different crate
Diffstat (limited to 'src')
-rw-r--r--src/bin/main.rs124
-rw-r--r--src/client.rs1187
-rw-r--r--src/error.rs94
-rw-r--r--src/helix/mod.rs83
-rw-r--r--src/helix/models.rs118
-rw-r--r--src/helix/namespaces/auth.rs19
-rw-r--r--src/helix/namespaces/clips.rs32
-rw-r--r--src/helix/namespaces/mod.rs26
-rw-r--r--src/helix/namespaces/users.rs44
-rw-r--r--src/helix/namespaces/videos.rs75
-rw-r--r--src/kraken/endpoints.rs0
-rw-r--r--src/kraken/mod.rs83
-rw-r--r--src/kraken/models.rs79
-rw-r--r--src/kraken/namespaces/clips.rs33
-rw-r--r--src/kraken/namespaces/mod.rs20
-rw-r--r--src/kraken/namespaces/users.rs32
-rw-r--r--src/lib.rs22
-rw-r--r--src/models.rs28
-rw-r--r--src/namespace/auth.rs55
-rw-r--r--src/namespace/mod.rs1
-rw-r--r--src/sync/barrier.rs47
-rw-r--r--src/sync/mod.rs2
-rw-r--r--src/sync/waiter.rs12
-rw-r--r--src/types.rs134
24 files changed, 0 insertions, 2350 deletions
diff --git a/src/bin/main.rs b/src/bin/main.rs
deleted file mode 100644
index a545ec1..0000000
--- a/src/bin/main.rs
+++ /dev/null
@@ -1,124 +0,0 @@
-extern crate dotenv;
-extern crate futures;
-extern crate serde;
-extern crate tokio;
-extern crate twitch_api;
-extern crate env_logger;
-
-use futures::future::Future;
-use std::env;
-use twitch_api::HelixClient;
-use twitch_api::KrakenClient;
-use twitch_api::ClientConfig;
-use twitch_api::client::RatelimitMap;
-
-
-fn main() {
- dotenv::dotenv().unwrap();
- env_logger::init();
-
- let config = ClientConfig {
- max_retrys: 0,
- ratelimits: RatelimitMap::empty(),
- ..ClientConfig::default()
- };
-
- let client_id = &env::var("TWITCH_API").unwrap();
- let helix_client = HelixClient::new_with_config(client_id, config);
- let kraken_client = KrakenClient::new(client_id);
-
- /*
- .authenticate(&env::var("TWITCH_SECRET").unwrap())
- .build();
- */
-
-/*
- let clip = helix_client
- .clips()
- .clip(&"EnergeticApatheticTarsierThisIsSparta")
- .map_err(|err| {
- println!("{:?}", err);
- ()
- });
- */
- /*
-
- let clip2 = authed_client
- .clips()
- .clip(&"EnergeticApatheticTarsierThisIsSparta")
- .map_err(|err| {
- println!("{:?}", err);
- ()
- });
- */
-
- //use twitch_api::types::VideoId;
-
- /*
- let videos = authed_client
- .videos()
- .by_user(&UserId::from_str("19571641").unwrap())
- .take(1)
- .for_each(|collection| {
- println!("{:?}", collection);
- Ok(())
- })
- .map(|_| ())
- .map_err(|err| {println!("{:?}", err); ()});
- */
-
-
-/*
- let clip2 = kraken_client
- .clips()
- .clip(&"EnergeticApatheticTarsierThisIsSparta")
- .map_err(|err| {
- println!("{:?}", err);
- ()
- });
-*/
-
-
- let f = futures::future::ok(1).and_then(move |_| {
- for i in 0..80 {
- let u = helix_client
- .users()
- .users(&vec!(), &vec!("freakey"))
- .map(|res| {println!("{:?}", res); ()})
- .map_err(|res| {println!("{:?}", res); ()});
- tokio::spawn(u);
- }
- Ok(())
- });
-
- /* 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(authed_client);
- tokio::run(
- f
- /*
- clip.join(clip2)
- .and_then(|(c1, c2)| {
- println!("{:?}", c1);
- println!("__");
- println!("{:?}", c2);
- Ok((c1, c2))
- }).and_then(move |_| {
- helix_client
- .clips()
- .clip(&ClipId::new("EnergeticApatheticTarsierThisIsSparta"))
- .map(|_| ())
- .map_err(|err| {
- println!("{:?}", err);
- ()
- })
- })
- .map(|_| ())
- .map_err(|_| ())
- */
- /*videos*/
- );
-}
diff --git a/src/client.rs b/src/client.rs
deleted file mode 100644
index 0307a05..0000000
--- a/src/client.rs
+++ /dev/null
@@ -1,1187 +0,0 @@
-use crate::models::Message;
-use std::convert::TryFrom;
-use futures::future::Future;
-use std::sync::{Arc, Mutex};
-use reqwest::r#async::Client as ReqwestClient;
-use reqwest::Error as ReqwestError;
-use reqwest::r#async::{Request, Response};
-
-use std::collections::{HashSet, HashMap};
-use super::error::Error;
-use futures::future::Shared;
-use futures::Poll;
-use serde::de::DeserializeOwned;
-use futures::Async;
-use futures::try_ready;
-use serde_json::Value;
-use futures::future::Either;
-
-use crate::error::ConditionError;
-
-pub use super::types;
-
-#[derive(PartialEq, Eq, Hash, Clone)]
-pub enum RatelimitKey {
- Default,
-}
-
-pub struct RatelimitMap {
- pub inner: HashMap<RatelimitKey, Ratelimit>
-}
-
-const API_DOMAIN: &'static str = "api.twitch.tv";
-const AUTH_DOMAIN: &'static str = "id.twitch.tv";
-const KRAKEN_ACCEPT: &'static str = "application/vnd.twitchtv.v5+json";
-
-pub trait PaginationTrait {
- fn cursor<'a>(&'a self) -> Option<&'a str>;
-}
-
-pub type ParamList<'a> = BTreeMap<&'a str, &'a dyn ToString>;
-
-#[derive(Clone)]
-pub struct Client {
- inner: Arc<ClientType>,
-}
-
-#[derive(Debug)]
-pub struct ScopeParseError {}
-use std::fmt;
-impl fmt::Display for ScopeParseError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "Scope Parse Error")
- }
-}
-
-/*TODO*/
-#[derive(PartialEq, Hash, Eq, Clone, Debug)]
-pub enum Scope {
- Helix(HelixScope),
- Kraken(KrakenScope),
-}
-
-impl TryFrom<&str> for Scope {
- type Error = ScopeParseError;
- fn try_from(s: &str) -> Result<Scope, Self::Error> {
- if let Ok(scope) = HelixScope::try_from(s) {
- return Ok(Scope::Helix(scope));
- }
- if let Ok(scope) = KrakenScope::try_from(s) {
- return Ok(Scope::Kraken(scope));
- }
- Err(ScopeParseError {})
- }
-}
-use serde::{Deserialize, Deserializer};
-impl<'de> Deserialize<'de> for Scope {
-
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where D: Deserializer<'de>
- {
- let id = String::deserialize(deserializer)?;
- Scope::try_from(&id[0..]).map_err(serde::de::Error::custom)
- }
-}
-
-#[derive(PartialEq, Hash, Eq, Clone, Debug)]
-pub enum HelixScope {
- AnalyticsReadExtensions,
- AnalyticsReadGames,
- BitsRead,
- ChannelReadSubscriptions,
- ClipsEdit,
- UserEdit,
- UserEditBroadcast,
- UserReadBroadcast,
- UserReadEmail,
-}
-
-impl HelixScope {
- pub fn to_str(&self) -> &'static str {
- use self::HelixScope::*;
- match self {
- AnalyticsReadExtensions => "analytics:read:extensions",
- AnalyticsReadGames => "analytics:read:games",
- BitsRead => "bits:read",
- ChannelReadSubscriptions => "channel:read:subscriptions",
- ClipsEdit => "clips:edit",
- UserEdit => "user:edit",
- UserEditBroadcast => "user:edit:broadcast",
- UserReadBroadcast => "user:read:broadcast",
- UserReadEmail => "user:read:email",
- }
- }
-}
-
-impl TryFrom<&str> for HelixScope {
- type Error = ScopeParseError;
- fn try_from(s: &str) -> Result<HelixScope, Self::Error> {
- use self::HelixScope::*;
- Ok( match s {
- "analytics:read:extensions" => AnalyticsReadExtensions,
- "analytics:read:games" => AnalyticsReadGames,
- "bits:read" => BitsRead,
- "channel:read:subscriptions" => ChannelReadSubscriptions,
- "clips:edit" => ClipsEdit,
- "user:edit" => UserEdit,
- "user:edit:broadcast" => UserEditBroadcast,
- "user:read:broadcast" => UserReadBroadcast,
- "user:read:email" => UserReadEmail,
- _ => return Err(ScopeParseError{})
- })
- }
-}
-
-#[derive(PartialEq, Hash, Eq, Clone, Debug)]
-pub enum KrakenScope {
- ChannelCheckSubscription,
- ChannelCommercial,
- ChannelEditor,
- ChannelFeedEdit,
- ChannelFeedRead,
- ChannelRead,
- ChannelStream,
- ChannelSubscriptions,
- CollectionsEdit,
- CommunitiesEdit,
- CommunitiesModerate,
- Openid,
- UserBlocksEdit,
- UserBlocksRead,
- UserFollowsEdit,
- UserRead,
- UserSubscriptions,
- ViewingActivityRead,
-}
-
-impl KrakenScope {
- pub fn to_str(&self) -> &'static str {
- use self::KrakenScope::*;
- match self {
- ChannelCheckSubscription => "channel_check_subscription",
- ChannelCommercial => "channel_commercial",
- ChannelEditor => "channel_editor",
- ChannelFeedEdit => "channel_feed_edit",
- ChannelFeedRead => "channel_feed_read",
- ChannelRead => "channel_read",
- ChannelStream => "channel_stream",
- ChannelSubscriptions => "channel_subscriptions",
- CollectionsEdit => "collections_edit",
- CommunitiesEdit => "communities_edit",
- CommunitiesModerate => "communities_moderate",
- Openid => "openid",
- UserBlocksEdit => "user_blocks_edit",
- UserBlocksRead => "user_blocks_read",
- UserFollowsEdit => "user_follows_edit",
- UserRead => "user_read",
- UserSubscriptions => "user_subscriptions",
- ViewingActivityRead => "viewing_activity_read",
- }
- }
-}
-
-impl TryFrom<&str> for KrakenScope {
- type Error = ScopeParseError;
- fn try_from(s: &str) -> Result<KrakenScope, Self::Error> {
- use self::KrakenScope::*;
- Ok( match s {
- "channel_check_subscription" => ChannelCheckSubscription,
- "channel_commercial" => ChannelCommercial,
- "channel_editor" => ChannelEditor,
- "channel_feed_edit" => ChannelFeedEdit,
- "channel_feed_read" => ChannelFeedRead,
- "channel_read" => ChannelRead,
- "channel_stream" => ChannelStream,
- "channel_subscriptions" => ChannelSubscriptions,
- "collections_edit" => CollectionsEdit,
- "communities_edit" => CommunitiesEdit,
- "communities_moderate" => CommunitiesModerate,
- "openid" => Openid,
- "user_blocks_edit" => UserBlocksEdit,
- "user_blocks_read" => UserBlocksRead,
- "user_follows_edit" => UserFollowsEdit,
- "user_read" => UserRead,
- "user_subscriptions" => UserSubscriptions,
- "viewing_activity_read" => ViewingActivityRead,
- _ => return Err(ScopeParseError {})
- })
- }
-}
-
-#[derive(Clone)]
-pub enum Version {
- Helix,
- Kraken,
-}
-
-impl Client {
-
- pub fn authenticate(self, secret: &str) -> AuthClientBuilder {
- AuthClientBuilder::new(self, secret)
- }
-
- pub fn deauthenticate(self) -> Client {
- use self::ClientType::*;
- match self.inner.as_ref() {
- Unauth(_inner) => self,
- Auth(inner) => inner.previous.clone(),
- }
- }
-}
-
-pub struct TestConfigRef {
- pub requests: Vec<Result<Request, ReqwestError>>,
- pub responses: Vec<Response>,
-}
-
-#[derive(Clone)]
-pub struct TestConfig {
- pub inner: Arc<Mutex<TestConfigRef>>
-}
-
-impl TestConfig {
-
- pub fn push_response(&self, response: Response) {
- let inner = &mut self.inner.lock().unwrap();
- inner.responses.push(response);
- }
-}
-
-impl Default for TestConfig {
-
- fn default() -> Self {
- TestConfig {
- inner: Arc::new(
- Mutex::new(
- TestConfigRef {
- requests: Vec::new(),
- responses: Vec::new(),
- }
- )
- )
- }
- }
-}
-
-enum ClientType {
- Unauth(UnauthClient),
- Auth(AuthClient),
-}
-
-
-pub struct ClientConfig {
- pub reqwest: ReqwestClient,
- pub domain: String,
- pub auth_domain: String,
- pub ratelimits: RatelimitMap,
- pub max_retrys: u32,
- pub test_config: Option<TestConfig>,
-}
-
-impl Default for RatelimitMap {
-
- fn default() -> Self {
- let mut limits = HashMap::new();
- limits.insert(RatelimitKey::Default, Ratelimit::new(30, "Ratelimit-Limit", "Ratelimit-Remaining", "Ratelimit-Reset"));
- RatelimitMap {
- inner: limits
- }
- }
-}
-
-impl RatelimitMap {
- pub fn empty() -> RatelimitMap {
- RatelimitMap {
- inner: HashMap::new()
- }
- }
-}
-
-impl Default for ClientConfig {
-
- fn default() -> Self {
- let reqwest = ReqwestClient::new();
- let ratelimits = RatelimitMap::default();
-
- ClientConfig {
- reqwest,
- domain: API_DOMAIN.to_owned(),
- auth_domain: AUTH_DOMAIN.to_owned(),
- ratelimits,
- max_retrys: 1,
- test_config: None,
- }
- }
-}
-
-pub struct UnauthClient {
- id: String,
- config: ClientConfig,
- version: Version,
-}
-
-pub struct AuthClient {
- secret: String,
- auth_state: Mutex<AuthStateRef>,
- auth_barrier: Barrier,
- previous: Client,
-}
-
-pub trait ClientTrait {
-
- fn id<'a>(&'a self) -> &'a str;
- fn config<'a>(&'a self) -> &'a ClientConfig;
- fn domain<'a>(&'a self) -> &'a str;
- fn auth_domain<'a>(&'a self) -> &'a str;
- fn ratelimit<'a>(&'a self, key: RatelimitKey) -> Option<&'a Ratelimit>;
-
- fn authenticated(&self) -> bool;
- fn scopes(&self) -> Vec<Scope>;
-}
-
-impl ClientTrait for UnauthClient {
- fn id<'a>(&'a self) -> &'a str {
- &self.id
- }
-
- fn domain<'a>(&'a self) -> &'a str {
- &self.config.domain
- }
-
- fn auth_domain<'a>(&'a self) -> &'a str {
- &self.config.auth_domain
- }
-
- fn ratelimit<'a>(&'a self, key: RatelimitKey) -> Option<&'a Ratelimit> {
- self.config.ratelimits.inner.get(&key)
- }
-
- fn authenticated(&self) -> bool {
- false
- }
-
- fn config<'a>(&'a self) -> &'a ClientConfig {
- &self.config
- }
-
- fn scopes(&self) -> Vec<Scope> {
- Vec::with_capacity(0)
- }
-}
-
-impl ClientTrait for Client {
-
- fn id<'a>(&'a self) -> &'a str {
- use self::ClientType::*;
- match self.inner.as_ref() {
- Unauth(inner) => inner.id(),
- Auth(inner) => inner.id(),
- }
- }
-
- fn domain<'a>(&'a self) -> &'a str {
- use self::ClientType::*;
- match self.inner.as_ref() {
- Unauth(inner) => inner.domain(),
- Auth(inner) => inner.domain(),
- }
- }
-
- fn auth_domain<'a>(&'a self) -> &'a str {
- use self::ClientType::*;
- match self.inner.as_ref() {
- Unauth(inner) => inner.auth_domain(),
- Auth(inner) => inner.auth_domain(),
- }
- }
-
- fn config<'a>(&'a self) -> &'a ClientConfig {
- use self::ClientType::*;
- match self.inner.as_ref() {
- Unauth(inner) => inner.config(),
- Auth(inner) => inner.config(),
- }
- }
-
- fn ratelimit<'a>(&'a self, key: RatelimitKey) -> Option<&'a Ratelimit> {
- use self::ClientType::*;
- match self.inner.as_ref() {
- Unauth(inner) => inner.ratelimit(key),
- Auth(inner) => inner.ratelimit(key),
- }
- }
-
- fn authenticated(&self) -> bool {
- use self::ClientType::*;
- match self.inner.as_ref() {
- Unauth(inner) => inner.authenticated(),
- Auth(inner) => inner.authenticated(),
- }
- }
-
- fn scopes(&self) -> Vec<Scope> {
- use self::ClientType::*;
- match self.inner.as_ref() {
- Unauth(inner) => inner.scopes(),
- Auth(inner) => inner.scopes(),
- }
- }
-}
-
-/*TODO I'd be nice to remove this boiler plate */
-impl ClientTrait for AuthClient {
- fn id<'a>(&'a self) -> &'a str {
- match self.previous.inner.as_ref() {
- ClientType::Auth(auth) => auth.id(),
- ClientType::Unauth(unauth) => unauth.id(),
- }
- }
-
- fn domain<'a>(&'a self) -> &'a str {
- match self.previous.inner.as_ref() {
- ClientType::Auth(auth) => auth.domain(),
- ClientType::Unauth(unauth) => unauth.domain(),
- }
- }
-
- fn auth_domain<'a>(&'a self) -> &'a str {
- match self.previous.inner.as_ref() {
- ClientType::Auth(auth) => auth.auth_domain(),
- ClientType::Unauth(unauth) => unauth.auth_domain(),
- }
- }
-
- fn config<'a>(&'a self) -> &'a ClientConfig {
- match self.previous.inner.as_ref() {
- ClientType::Auth(auth) => auth.config(),
- ClientType::Unauth(unauth) => unauth.config(),
- }
- }
-
- fn ratelimit<'a>(&'a self, key: RatelimitKey) -> Option<&'a Ratelimit> {
- match self.previous.inner.as_ref() {
- ClientType::Auth(auth) => auth.ratelimit(key),
- ClientType::Unauth(unauth) => unauth.ratelimit(key),
- }
- }
-
- fn authenticated(&self) -> bool {
- let auth = self.auth_state.lock().expect("Auth Lock is poisoned");
- auth.state == AuthState::Auth
- }
-
- fn scopes(&self) -> Vec<Scope> {
- let auth = self.auth_state.lock().expect("Auth Lock is poisoned");
- auth.scopes.clone()
- }
-}
-
-#[derive(Clone, PartialEq)]
-enum AuthState {
- Unauth,
- Auth,
-}
-
-struct AuthStateRef {
- token: Option<String>,
- scopes: Vec<Scope>,
- state: AuthState,
-}
-
-impl Client {
- pub fn new(id: &str, config: ClientConfig, version: Version) -> Client {
- let client = ReqwestClient::new();
- Client {
- inner: Arc::new(
- ClientType::Unauth(UnauthClient {
- id: id.to_owned(),
- config: config,
- version: version,
- }))
- }
- }
-
- fn secret<'a>(&'a self) -> Option<&'a str> {
- use self::ClientType::*;
- match self.inner.as_ref() {
- Unauth(_) => None,
- Auth(inner) => Some(&inner.secret),
- }
- }
-
- fn version(&self) -> Version {
- use self::ClientType::*;
- match self.inner.as_ref() {
- Unauth(inner) => inner.version.clone(),
- Auth(inner) => inner.previous.version(),
- }
- }
-
- fn reqwest(&self) -> ReqwestClient {
- use self::ClientType::*;
- match self.inner.as_ref() {
- Unauth(inner) => inner.config.reqwest.clone(),
- Auth(inner) => inner.previous.reqwest(),
- }
- }
-
- fn send(&self, builder: RequestBuilder) -> Box<dyn Future<Item=Response, Error=reqwest::Error> + Send> {
- if let Some(test_config) = &self.config().test_config {
- let config: &mut TestConfigRef = &mut test_config.inner.lock().expect("Test Config poisoned");
- println!("{}", config.responses.len());
- config.requests.push(builder.build());
- let res = config.responses.pop().expect("Ran out of test responses!");
- Box::new(futures::future::ok(res))
- } else {
- Box::new(builder.send())
- }
- }
-
- /* The 'bottom' client must always be a client that is not authorized.
- * This which allows for calls to Auth endpoints using the same control flow
- * as other requests.
- *
- * Clients created with 'new' are bottom clients and calls
- * to authenticate stack an authed client on top
- */
- fn get_bottom_client(&self) -> Client {
- match self.inner.as_ref() {
- ClientType::Auth(inner) => inner.previous.get_bottom_client(),
- ClientType::Unauth(_) => self.clone(),
- }
- }
-
- fn apply_standard_headers(&self, request: RequestBuilder)
- -> RequestBuilder
- {
- let token = match self.inner.as_ref() {
- ClientType::Auth(inner) => {
- let auth = inner.auth_state.lock().expect("Authlock is poisoned");
- auth.token.as_ref().map(|s| s.to_owned())
- }
- ClientType::Unauth(_) => None,
- };
- match self.version() {
- Version::Helix => {
-
- let client_header = header::HeaderValue::from_str(self.id()).unwrap();
-
- let request =
- if let Some(token) = token {
- let value = "Bearer ".to_owned() + &token;
- let token_header = header::HeaderValue::from_str(&value).unwrap();
- request.header("Authorization", token_header)
- } else { request };
-
- request.header("Client-ID", client_header)
- },
- Version::Kraken => {
- let client_header = header::HeaderValue::from_str(self.id()).unwrap();
- let accept_header = header::HeaderValue::from_str(KRAKEN_ACCEPT).unwrap();
-
- let request = request.header("Client-ID", client_header);
- let request = request.header("Accept", accept_header);
- let request = if let Some(token) = token {
- let value = "OAuth ".to_owned() + &token;
- let token_header = header::HeaderValue::from_str(&value).unwrap();
- request.header("Authorization", token_header)
- } else {request};
-
- request
- }
- }
- }
-}
-
-
-use reqwest::r#async::{RequestBuilder};
-use reqwest::header;
-
-
-pub struct AuthClientBuilder {
- scopes: HashSet<Scope>,
- secret: String,
- token: Option<String>,
- client: Client,
- /*If the user supplies a token,
- * then we can skip fetching it from the server and are authenticated
- */
-}
-
-impl AuthClientBuilder {
- pub fn new(client: Client, secret: &str) -> AuthClientBuilder {
- AuthClientBuilder {
- scopes: HashSet::new(),
- client: client,
- secret: secret.to_owned(),
- token: None,
- }
- }
-
- pub fn build(self) -> Client {
- let auth_state = if self.token.is_some() { AuthState::Auth } else { AuthState::Unauth };
- let old_client = self.client;
- Client {
- inner: Arc::new(ClientType::Auth(
- AuthClient {
- secret: self.secret,
- auth_barrier: Barrier::new(),
- auth_state: Mutex::new (
- AuthStateRef {
- token: self.token,
- scopes: Vec::new(),
- state: auth_state,
- }),
- previous: old_client,
- }))
- }
- }
-
- 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(mut self, token: &str) -> AuthClientBuilder {
- self.token.replace(token.to_owned());
- self
- }
-}
-
-use std::collections::BTreeMap;
-use reqwest::Method;
-
-struct RequestRef {
- url: String,
- params: BTreeMap<String, String>,
- client: Client,
- ratelimit: Option<RatelimitKey>,
- method: Method,
-}
-
-impl RequestRef {
- pub fn new(url: String,
- params: BTreeMap<&str, &dyn ToString>,
- client: Client,
- method: Method,
- ratelimit: Option<RatelimitKey>,
- ) -> RequestRef
- {
- let mut owned_params = BTreeMap::new();
- for (key, value) in params {
- owned_params.insert(key.to_string(), value.to_string());
- }
-
- RequestRef {
- url: url,
- params: owned_params,
- client: client,
- method: method,
- ratelimit: ratelimit,
- }
- }
-}
-
-enum RequestState<T> {
- SetupRequest,
- SetupBarriers,
- WaitAuth(WaiterState<AuthWaiter>),
- SetupRatelimit,
- WaitLimit(WaiterState<RatelimitWaiter>),
- WaitRequest,
- PollParse(Box<dyn Future<Item=T, Error=Error> + Send>),
-}
-
-pub struct ApiRequest<T> {
- inner: Arc<RequestRef>,
- state: RequestState<T>,
- attempt: u32,
- max_attempts: u32,
- pagination: Option<String>,
-}
-
-enum IterableApiRequestState<T> {
- Start,
- PollInner(ApiRequest<T>),
- Finished,
-}
-
-pub struct IterableApiRequest<T> {
- inner: Arc<RequestRef>,
- state: IterableApiRequestState<T>,
-}
-
-impl<T: DeserializeOwned + PaginationTrait + 'static + Send> ApiRequest<T> {
-
- pub fn new(url: String,
- params: BTreeMap<&str, &dyn ToString>,
- client: Client,
- method: Method,
- ratelimit: Option<RatelimitKey>,
- ) -> ApiRequest<T>
- {
- let max_attempts = client.config().max_retrys;
- ApiRequest {
- inner: Arc::new(RequestRef::new(url, params, client, method, ratelimit)),
- state: RequestState::SetupRequest,
- attempt: 0,
- max_attempts,
- pagination: None,
- }
- }
-}
-
-impl<T: DeserializeOwned + PaginationTrait + 'static + Send> IterableApiRequest<T> {
-
- pub fn new(url: String,
- params: BTreeMap<&str, &dyn ToString>,
- client: Client,
- method: Method,
- ratelimit: Option<RatelimitKey>
- ) -> IterableApiRequest<T>
- {
- let request_ref =
- Arc::new(RequestRef::new(url, params, client, method, ratelimit));
-
- IterableApiRequest {
- inner: request_ref,
- state: IterableApiRequestState::Start,
- }
- }
-}
-
-
-pub struct RatelimitWaiter {
- limit: Ratelimit,
-}
-
-#[derive(Clone)]
-pub struct Ratelimit {
- inner: Arc<Mutex<RatelimitRef>>,
- barrier: Barrier,
-}
-
-impl Ratelimit {
- pub fn new(limit: i32,
- header_limit: &str,
- header_remaining: &str,
- header_reset: &str)
- -> Ratelimit
- {
- Ratelimit {
- inner: Arc::new(
- Mutex::new(
- RatelimitRef {
- limit: limit,
- remaining: limit,
- inflight: 0,
- reset: None,
- header_limit: header_limit.to_owned(),
- header_remaining: header_remaining.to_owned(),
- header_reset: header_reset.to_owned(),
- }
- )
- ),
- barrier: Barrier::new(),
- }
- }
-}
-
-
-
-#[derive(Debug, Clone)]
-pub struct RatelimitRef {
- limit: i32,
- remaining: i32,
- inflight: i32,
- reset: Option<u32>,
- header_limit: String,
- header_remaining: String,
- header_reset: String,
-}
-
-
-impl RatelimitRef {
- pub fn update_from_headers(&mut self, headers: &reqwest::header::HeaderMap) {
- let maybe_limit =
- headers
- .get(&self.header_limit)
- .and_then(|x| x.to_str().ok())
- .and_then(|x| x.parse::<i32>().ok());
-
- if let Some(limit) = maybe_limit {
- self.limit = limit;
- }
-
- let maybe_remaining =
- headers
- .get(&self.header_remaining)
- .and_then(|x| x.to_str().ok())
- .and_then(|x| x.parse::<i32>().ok());
-
- if let Some(limit) = maybe_remaining {
- self.remaining = limit;
- }
-
- let maybe_reset =
- headers
- .get(&self.header_reset)
- .and_then(|x| x.to_str().ok())
- .and_then(|x| x.parse::<u32>().ok());
-
- if let Some(reset) = maybe_reset {
- self.reset = Some(reset);
- }
- }
-}
-
-use crate::sync::barrier::Barrier;
-use crate::sync::waiter::Waiter;
-
-struct WaiterState<W: Waiter> {
- polling: bool,
- shared_future: Option<(Shared<Box<Future<Item=(), Error=ConditionError> + Send>>)>,
- waiter: W,
- barrier: Barrier,
-}
-
-impl<W: Waiter> WaiterState<W> {
- fn new(waiter: W, barrier: &Barrier) -> WaiterState<W> {
- WaiterState {
- polling: false,
- shared_future: None,
- waiter: waiter,
- barrier: barrier.clone(),
- }
- }
-}
-
-impl<W: Waiter> Future for WaiterState<W> {
- type Item = <W as Waiter>::Item;
- type Error = <W as Waiter>::Error;
-
- fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
- loop {
- let blocked = self.waiter.blocked();
- if blocked && !self.polling {
- let fut = self.barrier.condition(&self.waiter);
- self.shared_future = Some(fut);
- self.polling = true;
- } else if blocked || self.polling {
- let f = self.shared_future.as_mut().unwrap();
- try_ready!(f.poll());
- self.polling = false;
- } else {
- return Ok(Async::Ready(<W as Waiter>::Item::default()));
- }
- }
- }
-}
-
-
-struct AuthWaiter {
- waiter: Client,
-}
-
-impl Waiter for AuthWaiter {
- type Item = ();
- type Error = ConditionError;
-
- fn blocked(&self) -> bool {
- match self.waiter.inner.as_ref() {
- ClientType::Unauth(_) => false,
- ClientType::Auth(inner) => {
- let auth = inner.auth_state.lock()
- .expect("unable to lock auth state");
- auth.state == AuthState::Unauth
- }
- }
- }
-
- fn condition(&self) ->
- Shared<Box<Future<Item=(), Error=ConditionError> + Send>> {
- /* If a secret is not provided then just immediately return */
- let secret = self.waiter.secret().unwrap();
- let bottom_client = self.waiter.get_bottom_client();
- let client = self.waiter.clone();
-
- let auth_future =
- bottom_client
- .auth()
- .client_credentials(secret)
- .map(move |credentials| {
- if let ClientType::Auth(inner) = client.inner.as_ref() {
- let mut auth = inner.auth_state.lock().unwrap();
- auth.state = AuthState::Auth;
- auth.token = Some(credentials.access_token.clone());
- if let Some(scopes) = credentials.scope {
- for scope in scopes { auth.scopes.push(scope) }
- }
- }
- ()
- })
- .map_err(|err| err.into());
-
- Future::shared(Box::new(auth_future))
- }
-}
-
-impl Waiter for RatelimitWaiter {
- type Item = ();
- type Error = ConditionError;
-
- fn blocked(&self) -> bool {
- let limits = self.limit.inner.lock().unwrap();
- limits.remaining - limits.inflight <= 0
- }
-
- fn condition(&self)
- -> Shared<Box<Future<Item=(), Error=ConditionError> + Send>>
- {
- /*TODO: Really basic for now*/
- use futures_timer::Delay;
- use std::time::Duration;
- let limits = self.limit.clone();
- Future::shared(Box::new(
- Delay::new(Duration::from_secs(60))
- .map(move |_res| {
- let mut limits = limits.inner.lock().unwrap();
- limits.remaining = limits.limit;
- ()
- })
- .map_err(|err| Error::from(err).into())
- ))
- }
-}
-
-/* Macro ripped directly from try_ready and simplies retries if any error occurs
- * and there are remaning retry attempt
- */
-#[macro_export]
-macro_rules! retry_ready {
- ($s:expr, $e:expr) => (match $e {
- Ok(futures::prelude::Async::Ready(t)) => t,
- Ok(futures::prelude::Async::NotReady) => return Ok(futures::prelude::Async::NotReady),
- Err(e) => {
- if $s.attempt < $s.max_attempts {
- $s.attempt += 1;
- $s.state = RequestState::SetupBarriers;
- continue;
- } else {
- return Err(e.into());
- }
- }
- })
-}
-
-use futures::Stream;
-
-impl<T: DeserializeOwned + PaginationTrait + 'static + Send> Stream for IterableApiRequest<T> {
- type Item = T;
- type Error = Error;
-
- fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
- loop {
- match &mut self.state {
- IterableApiRequestState::Start => {
- self.state =
- IterableApiRequestState::PollInner(
- ApiRequest {
- inner: self.inner.clone(),
- state: RequestState::SetupRequest,
- attempt: 0,
- max_attempts: self.inner.client.config().max_retrys,
- pagination: None
- });
- },
- IterableApiRequestState::PollInner(request) => {
- let f = request as &mut Future<Item=Self::Item, Error=Self::Error>;
- match f.poll() {
- Err(err) => {
- self.state = IterableApiRequestState::Finished;
- return Err(err);
- },
- Ok(state) => {
- match state {
- Async::NotReady => return Ok(Async::NotReady),
- Async::Ready(res) => {
- let cursor = res.cursor();
- match cursor {
- Some(cursor) => {
- self.state = IterableApiRequestState::PollInner(
- ApiRequest {
- inner: self.inner.clone(),
- state: RequestState::SetupRequest,
- attempt: 0,
- max_attempts: self.inner.client.config().max_retrys,
- pagination: Some(cursor.to_owned()),
- });
- },
- None => {
- self.state = IterableApiRequestState::Finished;
- }
- }
- return Ok(Async::Ready(Some(res)));
- }
- }
- }
- }
- },
- IterableApiRequestState::Finished => {
- return Ok(Async::Ready(None));
- }
- }
- }
- }
-}
-
-impl<T: DeserializeOwned + 'static + Send> Future for ApiRequest<T> {
- type Item = T;
- type Error = Error;
-
- fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
- loop {
- match &mut self.state {
- RequestState::SetupRequest => {
- self.attempt = 0;
- self.state = RequestState::SetupBarriers;
- }
- RequestState::SetupBarriers => {
- match self.inner.client.inner.as_ref() {
- ClientType::Auth(inner) => {
- let waiter = AuthWaiter {
- waiter: self.inner.client.clone(),
- };
-
- let f = WaiterState::new(waiter,
- &inner.auth_barrier);
- self.state = RequestState::WaitAuth(f);
- },
- ClientType::Unauth(_) => {
- self.state = RequestState::SetupRatelimit;
- }
- }
- },
- RequestState::WaitAuth(auth) => {
- let _waiter = retry_ready!(self, auth.poll());
- self.state = RequestState::SetupRatelimit;
- },
- RequestState::SetupRatelimit => {
- let limits =
- self.inner.ratelimit.as_ref().and_then(|key| {
- self.inner.client.ratelimit(key.clone())
- });
- match limits {
- Some(ratelimit) => {
- let barrier = ratelimit.barrier.clone();
- let waiter = RatelimitWaiter {
- limit: ratelimit.clone(),
- };
- let f = WaiterState::new(waiter,
- &barrier);
- self.state = RequestState::WaitLimit(f);
- },
- None => {
- self.state = RequestState::WaitRequest;
- }
- }
- },
- RequestState::WaitLimit(limit) => {
- let _waiter = retry_ready!(self, limit.poll());
- self.state = RequestState::WaitRequest;
- },
- RequestState::WaitRequest => {
- let client = self.inner.client.clone();
- let c_ref = &client;
- let reqwest = client.reqwest();
-
- let limits =
- self.inner.ratelimit.as_ref().and_then(|key| {
- c_ref.ratelimit(key.clone())
- });
-
- if let Some(limits) = limits {
- let mut mut_limits = limits.inner.lock().unwrap();
- mut_limits.inflight = mut_limits.inflight + 1;
- }
-
- let mut builder = reqwest.request(self.inner.method.clone(), &self.inner.url);
- builder = client.apply_standard_headers(builder);
- builder = builder.query(&self.inner.params);
- builder =
- if let Some(cursor) = &self.pagination {
- builder.query(&[("after", cursor)])
- } else {
- builder
- };
-
-
- let ratelimit_key = self.inner.ratelimit.clone();
- let client_cloned = client.clone();
- /*
- Allow testing by capturing the request and returning a predetermined response
- If testing is set in the client config then `Pending` is captured and saved and a future::ok(Resposne) is returned.
- */
- let f =
- client.send(builder)
- .then(move |result| {
- trace!("[TWITCH_API] {:?}", result);
- if let Some(ratelimit_key) = ratelimit_key {
- if let Some(limits) = client_cloned.ratelimit(ratelimit_key) {
- let mut mut_limits = limits.inner.lock().unwrap();
- mut_limits.inflight = mut_limits.inflight - 1;
- }
- }
- result
- })
- .map_err(|err| err.into())
- .and_then(|mut response| {
- let status = response.status();
- if status.is_success() {
- Either::A(
- response.json().map_err(|err| Error::from(err)).and_then(|json| {
- trace!("[TWITCH_API] {}", json);
- serde_json::from_value(json).map_err(|err| err.into())
- })
- )
- } else {
- Either::B(
- response.json::<Message>()
- .then(|res| {
- match res {
- Ok(message) => futures::future::err(Some(message)),
- Err(_err) => futures::future::err(None)
- }
- })
- .map_err(move |maybe_message| {
- let status = response.status();
- if status == 401 || status == 403 {
- Error::auth_error(maybe_message)
- } else if status == 429 {
- Error::ratelimit_error(maybe_message)
- } else {
- Error::auth_error(maybe_message)
- }
- })
- )
- }
- });
- self.state = RequestState::PollParse(Box::new(f));
- },
- RequestState::PollParse(future) => {
- let res = retry_ready!(self, future.poll());
- return Ok(Async::Ready(res));
- },
- }
- }
- }
-} \ No newline at end of file
diff --git a/src/error.rs b/src/error.rs
deleted file mode 100644
index c291b5d..0000000
--- a/src/error.rs
+++ /dev/null
@@ -1,94 +0,0 @@
-use reqwest::Error as ReqwestError;
-use futures::future::SharedError;
-use std::convert::From;
-use std::sync::Arc;
-use serde_json::Error as JsonError;
-use crate::models::Message;
-
-#[derive(Clone, Debug)]
-pub struct ConditionError {
- inner: Arc<Error>,
-}
-
-impl From<SharedError<ConditionError>> for ConditionError {
- fn from(other: SharedError<ConditionError>) -> Self {
- (*other).clone()
- }
-}
-
-impl From<Error> for ConditionError {
- fn from(other: Error) -> Self {
- ConditionError{ inner: Arc::new(other) }
- }
-}
-
-#[derive(Debug)]
-enum Kind {
- Reqwest(ReqwestError),
- ConditionError(ConditionError),
- Io(std::io::Error),
- Json(JsonError),
- AuthError(Option<Message>),
- RatelimitError(Option<Message>),
-}
-
-#[derive(Debug)]
-pub struct Error {
- inner: Kind
-}
-
-impl Error {
- pub fn auth_error(message: Option<Message>) -> Error {
- Error { inner: Kind::AuthError(message) }
- }
-
- pub fn ratelimit_error(message: Option<Message>) -> Error {
- Error { inner: Kind::RatelimitError(message) }
- }
-
- pub fn is_auth_error(&self) -> bool {
- match &self.inner {
- Kind::AuthError(_) => true,
- Kind::ConditionError(condition) => condition.inner.is_auth_error(),
- _ => false,
- }
- }
-
- pub fn is_ratelimit_error(&self) -> bool {
- match &self.inner {
- Kind::RatelimitError(_) => true,
- Kind::ConditionError(condition) => condition.inner.is_ratelimit_error(),
- _ => false,
- }
- }
-}
-
-
-impl From<reqwest::Error> for Error {
-
- fn from(err: ReqwestError) -> Error {
- Error {
- inner: Kind::Reqwest(err)
- }
- }
-}
-
-impl From<std::io::Error> for Error {
- fn from(err: std::io::Error) -> Self {
- Error { inner: Kind::Io(err) }
- }
-}
-
-impl From<JsonError> for Error {
-
- fn from(err: JsonError) -> Error {
- Error { inner: Kind::Json(err) }
- }
-}
-
-impl From<ConditionError> for Error {
-
- fn from(err: ConditionError) -> Error {
- Error { inner: Kind::ConditionError(err) }
- }
-}
diff --git a/src/helix/mod.rs b/src/helix/mod.rs
deleted file mode 100644
index da24aa4..0000000
--- a/src/helix/mod.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-use crate::client::Client as GenericClient;
-use crate::client::{Version, ClientConfig};
-use crate::client::ClientTrait;
-
-use crate::client::{HelixScope, Scope};
-
-pub mod models;
-pub mod namespaces;
-
-
-#[derive(Clone)]
-pub struct Client {
- inner: GenericClient
-}
-
-impl Client {
- pub fn new(id: &str) -> Client {
- let config = ClientConfig::default();
- Client {
- inner: GenericClient::new(id, config, Version::Helix)
- }
- }
-
- pub fn new_with_config(id: &str, config: ClientConfig) -> Client {
- Client {
- inner: GenericClient::new(id, config, Version::Helix)
- }
- }
-
- pub fn authenticate(self, secret: &str) -> AuthClientBuilder {
- AuthClientBuilder::new(self, secret)
- }
-
- pub fn id<'a>(&'a self) -> &'a str { &self.inner.id() }
- pub fn domain<'a>(&'a self) -> &'a str { &self.inner.domain() }
- pub fn auth_domain<'a>(&'a self) -> &'a str { &self.inner.auth_domain() }
- pub fn authenticated(&self) -> bool { self.inner.authenticated() }
-
- pub fn scopes(&self) -> Vec<HelixScope> {
- self.inner.scopes().into_iter().filter_map(|item| {
- if let Scope::Helix(scope) = item { Some(scope) } else { None }
- }).collect()
- }
-}
-
-use crate::client::AuthClientBuilder as GenericAuthClientBuilder;
-
-pub struct AuthClientBuilder {
- inner: GenericAuthClientBuilder,
-}
-
-impl AuthClientBuilder {
- pub fn new(client: Client, secret: &str) -> AuthClientBuilder {
- AuthClientBuilder {
- inner: GenericAuthClientBuilder::new(client.inner, secret),
- }
- }
-
- pub fn build(self) -> Client {
- let client = self.inner.build();
- Client {
- inner: client
- }
- }
-
- pub fn scope(self, scope: HelixScope) -> AuthClientBuilder {
- AuthClientBuilder {
- inner: self.inner.scope(Scope::Helix(scope))
- }
- }
-
- pub fn scopes(self, scopes: Vec<HelixScope>) -> AuthClientBuilder {
- AuthClientBuilder {
- inner: self.inner.scopes(scopes.into_iter().map(|e| Scope::Helix(e)).collect())
- }
- }
-
- pub fn token(self, token: &str) -> AuthClientBuilder {
- AuthClientBuilder {
- inner: self.inner.token(token)
- }
- }
-}
diff --git a/src/helix/models.rs b/src/helix/models.rs
deleted file mode 100644
index 1e9f0ec..0000000
--- a/src/helix/models.rs
+++ /dev/null
@@ -1,118 +0,0 @@
-extern crate serde_json;
-extern crate chrono;
-
-use url::Url;
-use chrono::{DateTime, Utc};
-use crate::types::{UserId, VideoId, ChannelId};
-
-use crate::client::PaginationTrait;
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct DataContainer<T> {
- pub data: Vec<T>
-}
-
-impl<T> PaginationTrait for DataContainer<T> {
- fn cursor<'a>(&'a self) -> Option<&'a str> { None }
-}
-
-impl<T> PaginationTrait for PaginationContainer<T> {
- fn cursor<'a>(&'a self) -> Option<&'a str> {
- match self.pagination.as_ref() {
- Some(cursor) => {
- match cursor.cursor.as_ref() {
- Some(cursor) => Some(cursor),
- None => None,
- }
- },
- None => None
- }
- }
-}
-
-impl PaginationTrait for Credentials {
- fn cursor<'a>(&'a self) -> Option<&'a str> { None }
-}
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct PaginationContainer<T> {
- pub data: Vec<T>,
- pub pagination: Option<Cursor>
-}
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct Cursor {
- pub cursor: Option<String>
-}
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct Video {
- pub id: VideoId,
- pub user_id: UserId,
- pub user_name: String,
- pub title: String,
- pub description: String,
- pub created_at: DateTime<Utc>,
- pub published_at: DateTime<Utc>,
- #[serde(with = "url_serde")]
- pub url: Url,
- /*FIXME: Serde will attempt to parse an empty string.
- * In this case this should be None when thumbnail_url is an empty string
- */
- //#[serde(with = "url_serde")]
- pub thumbnail_url: String, //Option<Url>,
- pub viewable: String,
- pub view_count: i32,
- pub language: String,
- #[serde(rename = "type")]
- pub video_type: String,
- //Should be converted to a Duration
- pub duration: String,
-}
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct User {
- pub id: UserId,
- pub login: String,
- pub display_name: String,
- #[serde(rename = "type")]
- pub user_type: String,
- pub broadcaster_type: String,
- pub description: String,
- #[serde(with = "url_serde")]
- pub profile_image_url: Url,
- //#[serde(with = "url_serde")]
- pub offline_image_url: String, // Option<Url>,
- pub view_count: u32,
- pub email: Option<String>,
-}
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct Clip {
- pub id: String,
- #[serde(with = "url_serde")]
- pub url: Url,
- #[serde(with = "url_serde")]
- pub embed_url: Url,
- pub broadcaster_id: ChannelId,
- pub broadcaster_name: String,
- pub creator_id: UserId,
- pub creator_name: String,
- pub video_id: VideoId,
- pub game_id: String,
- pub language: String,
- pub title: String,
- pub created_at: DateTime<Utc>,
- #[serde(with = "url_serde")]
- pub thumbnail_url: Url,
- pub view_count: i32,
-}
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct Credentials {
- pub access_token: String,
- pub refresh_token: Option<String>,
- pub expires_in: u32,
- pub scope: Option<Vec<String>>,
- pub token_type: String,
-}
diff --git a/src/helix/namespaces/auth.rs b/src/helix/namespaces/auth.rs
deleted file mode 100644
index d006e2f..0000000
--- a/src/helix/namespaces/auth.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-use super::*;
-use crate::models::Credentials;
-use crate::namespace::auth;
-
-pub struct Auth {}
-type AuthNamespace = Namespace<Auth>;
-
-impl AuthNamespace {
- pub fn client_credentials(self, secret: &str)
- -> ApiRequest<Credentials> {
- auth::client_credentials(self.client.inner, &secret)
- }
-}
-
-impl Client {
- pub fn auth(&self) -> AuthNamespace {
- AuthNamespace::new(self)
- }
-}
diff --git a/src/helix/namespaces/clips.rs b/src/helix/namespaces/clips.rs
deleted file mode 100644
index c4595ce..0000000
--- a/src/helix/namespaces/clips.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-use super::*;
-use super::models::{DataContainer, Clip};
-
-pub struct Clips {}
-type ClipsNamespace = Namespace<Clips>;
-
-impl ClipsNamespace {
- pub fn clip<S: ToString>(self, id: &S) -> ApiRequest<DataContainer<Clip>> {
- use self::clip;
- clip(self.client, id)
- }
-}
-
-impl Client {
- pub fn clips(&self) -> ClipsNamespace {
- ClipsNamespace::new(self)
- }
-}
-
-
-pub fn clip<S: ToString>(client: Client, id: &S)
- -> ApiRequest<DataContainer<Clip>>
-{
- let client = client.inner;
- let url =
- String::from("https://") +
- client.domain() + "/helix/clips" + "?id=" + &id.to_string();
-
- let params : ParamList = BTreeMap::new();
-
- ApiRequest::new(url, params, client, Method::GET, Some(RatelimitKey::Default))
-}
diff --git a/src/helix/namespaces/mod.rs b/src/helix/namespaces/mod.rs
deleted file mode 100644
index 1d4239a..0000000
--- a/src/helix/namespaces/mod.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use std::marker::PhantomData;
-
-pub use super::Client;
-pub use crate::client::{RatelimitKey, ClientTrait, ApiRequest, IterableApiRequest, ParamList};
-pub use std::collections::BTreeMap;
-pub use reqwest::Method;
-pub use super::models;
-
-pub mod clips;
-pub mod users;
-pub mod videos;
-pub mod auth;
-
-pub struct Namespace<T> {
- client: Client,
- _type: PhantomData<T>
-}
-
-impl<T> Namespace<T> {
- pub fn new(client: &Client) -> Self {
- Namespace {
- client: client.clone(),
- _type: PhantomData,
- }
- }
-}
diff --git a/src/helix/namespaces/users.rs b/src/helix/namespaces/users.rs
deleted file mode 100644
index 2e922d6..0000000
--- a/src/helix/namespaces/users.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-use super::*;
-use super::models::{DataContainer, User};
-use std::string::ToString;
-
-pub struct Users {}
-type UsersNamespace = Namespace<Users>;
-
-impl UsersNamespace {
- pub fn users<S: ToString>(self, ids: &[S], logins: &[S]) -> ApiRequest<DataContainer<User>> {
- use self::users;
- users(self.client, ids, logins)
- }
-}
-
-impl Client {
- pub fn users(&self) -> UsersNamespace {
- UsersNamespace::new(self)
- }
-}
-
-pub fn users<S: ToString>(
- client: Client,
- ids: &[S],
- logins: &[S],
- ) -> ApiRequest<DataContainer<User>> {
- let client = client.inner;
- let url =
- String::from("https://") + client.domain() + &String::from("/helix/users");
-
- let mut params: BTreeMap<&str, &dyn ToString> = BTreeMap::new();
- for id in ids {
- params.insert("id", id);
- }
-
- for login in logins {
- params.insert("login", login);
- }
-
- ApiRequest::new(url, params, client, Method::GET, Some(RatelimitKey::Default))
-}
-
-pub fn authed_as(client: Client) -> ApiRequest<DataContainer<User>> {
- users(client, &[""], &[""])
-}
diff --git a/src/helix/namespaces/videos.rs b/src/helix/namespaces/videos.rs
deleted file mode 100644
index b603b8f..0000000
--- a/src/helix/namespaces/videos.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-use super::*;
-use super::models::{PaginationContainer, Video};
-use crate::types::{UserId, GameId, VideoId};
-
-
-pub struct Videos {}
-type VideosNamespace = Namespace<Videos>;
-
-impl VideosNamespace {
- pub fn by_id<S: ToString>(self, ids: &[S])
- -> IterableApiRequest<PaginationContainer<Video>> {
- use self::by_id;
- by_id(self.client, ids)
- }
-
- pub fn by_user<S: ToString>(self, user_id: &S)
- -> IterableApiRequest<PaginationContainer<Video>> {
- use self::by_user;
- by_user(self.client, user_id)
- }
-
- pub fn for_game<S: ToString>(self, game_id: &S)
- -> IterableApiRequest<PaginationContainer<Video>> {
- use self::for_game;
- for_game(self.client, game_id)
- }
-}
-
-impl Client {
-
- pub fn videos(&self) -> VideosNamespace {
- VideosNamespace::new(self)
- }
-}
-
-pub fn by_id<S: ToString>(client: Client, ids: &[S])
- -> IterableApiRequest<PaginationContainer<Video>> {
- let client = client.inner;
- let url =
- String::from("https://") + client.domain() + &String::from("/helix/videos");
-
- let mut params: ParamList = BTreeMap::new();
- for id in ids {
- params.insert("id", id);
- }
-
- IterableApiRequest::new(url, params, client,
- Method::GET, Some(RatelimitKey::Default))
-}
-
-pub fn by_user<S: ToString>(client: Client, user_id: &S)
- -> IterableApiRequest<PaginationContainer<Video>> {
- let client = client.inner;
- let url =
- String::from("https://") + client.domain() + &String::from("/helix/videos");
-
- let mut params: ParamList = BTreeMap::new();
- params.insert("user_id", user_id);
-
- IterableApiRequest::new(url, params, client,
- Method::GET, Some(RatelimitKey::Default))
-}
-
-pub fn for_game<S: ToString>(client: Client, game_id: &S)
- -> IterableApiRequest<PaginationContainer<Video>> {
- let client = client.inner;
- let url =
- String::from("https://") + client.domain() + &String::from("/helix/videos");
-
- let mut params: ParamList = BTreeMap::new();
- params.insert("game_id", game_id);
-
- IterableApiRequest::new(url, params, client,
- Method::GET, Some(RatelimitKey::Default))
-}
diff --git a/src/kraken/endpoints.rs b/src/kraken/endpoints.rs
deleted file mode 100644
index e69de29..0000000
--- a/src/kraken/endpoints.rs
+++ /dev/null
diff --git a/src/kraken/mod.rs b/src/kraken/mod.rs
deleted file mode 100644
index 4046377..0000000
--- a/src/kraken/mod.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-use crate::client::Client as GenericClient;
-use crate::client::{Version, ClientConfig};
-use crate::client::ClientTrait;
-pub use super::types;
-
-use crate::client::{KrakenScope, Scope};
-
-mod namespaces;
-pub mod models;
-
-#[derive(Clone)]
-pub struct Client {
- inner: GenericClient
-}
-
-impl Client {
- pub fn new(id: &str) -> Client {
- let config = ClientConfig::default();
- Client {
- inner: GenericClient::new(id, config, Version::Kraken)
- }
- }
-
- pub fn new_with_config(id: &str, config: ClientConfig) -> Client {
- Client {
- inner: GenericClient::new(id, config, Version::Kraken)
- }
- }
-
- pub fn authenticate(self, secret: &str) -> AuthClientBuilder {
- AuthClientBuilder::new(self, secret)
- }
-
- pub fn id<'a>(&'a self) -> &'a str { &self.inner.id() }
- pub fn domain<'a>(&'a self) -> &'a str { &self.inner.domain() }
- pub fn auth_domain<'a>(&'a self) -> &'a str { &self.inner.auth_domain() }
- pub fn authenticated(&self) -> bool { self.inner.authenticated() }
-
- pub fn scopes(&self) -> Vec<KrakenScope> {
- self.inner.scopes().into_iter().filter_map(|item| {
- if let Scope::Kraken(scope) = item { Some(scope) } else { None }
- }).collect()
- }
-}
-
-use crate::client::AuthClientBuilder as GenericAuthClientBuilder;
-
-pub struct AuthClientBuilder {
- inner: GenericAuthClientBuilder,
-}
-
-impl AuthClientBuilder {
- pub fn new(client: Client, secret: &str) -> AuthClientBuilder {
- AuthClientBuilder {
- inner: GenericAuthClientBuilder::new(client.inner, secret),
- }
- }
-
- pub fn build(self) -> Client {
- let client = self.inner.build();
- Client {
- inner: client
- }
- }
-
- pub fn scope(self, scope: Scope) -> AuthClientBuilder {
- AuthClientBuilder {
- inner: self.inner.scope(scope)
- }
- }
-
- pub fn scopes(self, scopes: Vec<Scope>) -> AuthClientBuilder {
- AuthClientBuilder {
- inner: self.inner.scopes(scopes)
- }
- }
-
- pub fn token(self, token: &str) -> AuthClientBuilder {
- AuthClientBuilder {
- inner: self.inner.token(token)
- }
- }
-}
diff --git a/src/kraken/models.rs b/src/kraken/models.rs
deleted file mode 100644
index 931f849..0000000
--- a/src/kraken/models.rs
+++ /dev/null
@@ -1,79 +0,0 @@
-extern crate serde_json;
-extern crate chrono;
-extern crate url;
-
-use url::Url;
-use chrono::{DateTime, Utc};
-use super::types::{UserId, VideoId};
-use crate::client::PaginationTrait;
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct User {
- pub _id: String,
- pub bio: String,
- pub created_at: DateTime<Utc>,
- pub display_name: String,
- #[serde(with = "url_serde")]
- pub logo: Url,
- pub name: String,
- #[serde(rename = "type")]
- pub user_type: String,
- pub updated_at: DateTime<Utc>,
-}
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct Clip {
- pub slug: String,
- pub tracking_id: String,
- #[serde(with = "url_serde")]
- pub url: Url,
- #[serde(with = "url_serde")]
- pub embed_url: Url,
- pub embed_html: String,
- pub broadcaster: UserData,
- pub curator: UserData,
- pub vod: Vod,
- pub game: String,
- pub language: String,
- pub title: String,
- pub views: i32,
- pub duration: f32,
- pub created_at: DateTime<Utc>,
- pub thumbnails: Thumbnails,
-}
-
-impl PaginationTrait for Clip {
- fn cursor<'a>(&'a self) -> Option<&'a str> { None }
-}
-
-impl PaginationTrait for User {
- fn cursor<'a>(&'a self) -> Option<&'a str> { None }
-}
-
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct Thumbnails {
- #[serde(with = "url_serde")]
- pub medium: Url,
- #[serde(with = "url_serde")]
- pub small: Url,
- #[serde(with = "url_serde")]
- pub tiny: Url,
-}
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct UserData {
- pub id: UserId,
- pub name: String,
- pub display_name: String,
- #[serde(with = "url_serde")]
- pub channel_url: Url,
- pub logo: String,
-}
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct Vod {
- pub id: VideoId,
- #[serde(with = "url_serde")]
- pub url: Url,
-}
diff --git a/src/kraken/namespaces/clips.rs b/src/kraken/namespaces/clips.rs
deleted file mode 100644
index edde25b..0000000
--- a/src/kraken/namespaces/clips.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-use std::collections::BTreeMap;
-use super::super::models::{Clip};
-use super::super::Client;
-use crate::client::{RatelimitKey, ClientTrait, ApiRequest};
-use reqwest::Method;
-use super::Namespace;
-
-pub struct Clips {}
-type ClipsNamespace = Namespace<Clips>;
-
-impl ClipsNamespace {
- pub fn clip(self, id: &str) -> ApiRequest<Clip> {
- use self::clip;
- clip(self.client, id)
- }
-}
-
-impl Client {
-
- pub fn clips(&self) -> ClipsNamespace {
- ClipsNamespace::new(self)
- }
-}
-
-pub fn clip(client: Client, id: &str)
- -> ApiRequest<Clip>
-{
- let client = client.inner;
- let url = String::from("https://") + client.domain() + "/kraken/clips/" + id;
- let params = BTreeMap::new();
-
- ApiRequest::new(url, params, client, Method::GET, Some(RatelimitKey::Default))
-}
diff --git a/src/kraken/namespaces/mod.rs b/src/kraken/namespaces/mod.rs
deleted file mode 100644
index d8a065f..0000000
--- a/src/kraken/namespaces/mod.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-use std::marker::PhantomData;
-pub use super::models;
-pub use super::Client;
-
-pub mod clips;
-pub mod users;
-
-pub struct Namespace<T> {
- client: Client,
- _type: PhantomData<T>
-}
-
-impl<T> Namespace<T> {
- pub fn new(client: &Client) -> Self {
- Namespace {
- client: client.clone(),
- _type: PhantomData,
- }
- }
-}
diff --git a/src/kraken/namespaces/users.rs b/src/kraken/namespaces/users.rs
deleted file mode 100644
index d4adb8e..0000000
--- a/src/kraken/namespaces/users.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-use std::collections::BTreeMap;
-use super::super::models::{User};
-use super::super::Client;
-use crate::client::{RatelimitKey, ClientTrait, ApiRequest};
-use reqwest::Method;
-use super::Namespace;
-
-pub struct Users {}
-type UsersNamespace = Namespace<Users>;
-
-impl UsersNamespace {
- pub fn by_id(self, id: &str) -> ApiRequest<User> {
- use self::by_id;
- by_id(self.client, id)
- }
-}
-
-impl Client {
- pub fn users(&self) -> UsersNamespace {
- UsersNamespace::new(self)
- }
-}
-
-pub fn by_id(client: Client, id: &str)
- -> ApiRequest<User>
-{
- let client = client.inner;
- let url = String::from("https://") + client.domain() + "/kraken/users/" + id;
- let params = BTreeMap::new();
-
- ApiRequest::new(url, params, client, Method::GET, Some(RatelimitKey::Default))
-}
diff --git a/src/lib.rs b/src/lib.rs
deleted file mode 100644
index 35361ed..0000000
--- a/src/lib.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-#![recursion_limit="128"]
-#![feature(never_type)]
-extern crate futures;
-extern crate reqwest;
-extern crate serde;
-extern crate chrono;
-extern crate serde_json;
-#[macro_use] extern crate serde_derive;
-#[macro_use] extern crate log;
-
-pub mod helix;
-pub mod kraken;
-pub mod types;
-pub mod error;
-mod sync;
-pub mod namespace;
-pub mod client;
-pub mod models;
-
-pub use self::helix::Client as HelixClient;
-pub use self::kraken::Client as KrakenClient;
-pub use self::client::{ClientConfig, TestConfig};
diff --git a/src/models.rs b/src/models.rs
deleted file mode 100644
index cc5442c..0000000
--- a/src/models.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-extern crate serde_json;
-
-use crate::client::PaginationTrait;
-use crate::client::Scope;
-
-impl PaginationTrait for Credentials {
- fn cursor<'a>(&'a self) -> Option<&'a str> { None }
-}
-
-impl PaginationTrait for Message {
- fn cursor<'a>(&'a self) -> Option<&'a str> { None }
-}
-
-#[derive(Debug, Deserialize)]
-pub struct Credentials {
- pub access_token: String,
- pub refresh_token: Option<String>,
- pub expires_in: u32,
- pub scope: Option<Vec<Scope>>,
- pub token_type: String,
-}
-
-#[derive(Debug, Deserialize)]
-pub struct Message {
- pub error: Option<String>,
- pub message: String,
- pub status: u32,
-} \ No newline at end of file
diff --git a/src/namespace/auth.rs b/src/namespace/auth.rs
deleted file mode 100644
index ff835bf..0000000
--- a/src/namespace/auth.rs
+++ /dev/null
@@ -1,55 +0,0 @@
-use std::collections::BTreeMap;
-use crate::models::Credentials;
-use crate::client::Client;
-use crate::client::{ClientTrait, ApiRequest};
-use reqwest::Method;
-use std::marker::PhantomData;
-
-pub struct Namespace<T> {
- client: Client,
- _type: PhantomData<T>
-}
-
-impl<T> Namespace<T> {
- pub fn new(client: &Client) -> Self {
- Namespace {
- client: client.clone(),
- _type: PhantomData,
- }
- }
-}
-
-pub struct Auth {}
-type AuthNamespace = Namespace<Auth>;
-
-impl AuthNamespace {
- pub fn client_credentials(self, secret: &str)
- -> ApiRequest<Credentials> {
- use self::client_credentials;
- client_credentials(self.client, &secret.to_owned())
- }
-}
-
-impl Client {
- pub fn auth(&self) -> AuthNamespace {
- AuthNamespace::new(self)
- }
-}
-
-//TODO: Implement scopes
-pub fn client_credentials<S: ToString>(client: Client, secret: &S)
- -> ApiRequest<Credentials> {
-
- let url =
- String::from("https://") +
- client.auth_domain() + "/oauth2/token";
-
- let mut params : BTreeMap<&str, &dyn ToString> = BTreeMap::new();
- let client_id = &client.id();
- params.insert("client_id", &client_id);
- params.insert("client_secret", secret);
- params.insert("grant_type", &"client_credentials");
- params.insert("scope", &"");
-
- ApiRequest::new(url, params, client.clone(), Method::POST, None)
-}
diff --git a/src/namespace/mod.rs b/src/namespace/mod.rs
deleted file mode 100644
index 0e4a05d..0000000
--- a/src/namespace/mod.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub mod auth;
diff --git a/src/sync/barrier.rs b/src/sync/barrier.rs
deleted file mode 100644
index c0f21b8..0000000
--- a/src/sync/barrier.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-use super::waiter::Waiter;
-use futures::prelude::*;
-use std::sync::{Arc, Mutex};
-use futures::future::{Shared, SharedError};
-
-use crate::error::ConditionError;
-
-#[derive(Clone)]
-pub struct Barrier {
- inner: Arc<Mutex<BarrierRef>>,
-}
-
-struct BarrierRef {
- condition: Option<Shared<Box<Future<Item=(), Error=ConditionError> + Send>>>
-}
-
-impl Barrier {
-
- pub fn new() -> Barrier {
- Barrier {
- inner: Arc::new(Mutex::new(
- BarrierRef {
- condition: None,
- }))
- }
- }
-
- pub fn condition(&self, waiter: &impl Waiter)
- -> Shared<Box<Future<Item=(), Error=ConditionError> + Send>>
- {
- let mut mut_barrier = self.inner.lock().unwrap();
- let maybe_condition = &mut mut_barrier.condition;
-
- let f = maybe_condition.get_or_insert_with(|| {
- waiter.condition()
- });
-
- let f =
- if let Some(_) = f.peek() {
- let condition = waiter.condition();
- maybe_condition.replace(condition);
- maybe_condition.as_ref().unwrap()
- } else { f };
- f.clone()
- }
-}
-
diff --git a/src/sync/mod.rs b/src/sync/mod.rs
deleted file mode 100644
index 74e4235..0000000
--- a/src/sync/mod.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-pub mod barrier;
-pub mod waiter;
diff --git a/src/sync/waiter.rs b/src/sync/waiter.rs
deleted file mode 100644
index 1005e3d..0000000
--- a/src/sync/waiter.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-use futures::Future;
-use futures::future::{Shared, SharedError};
-use crate::error::ConditionError;
-
-pub trait Waiter {
- type Item: Default;
- type Error: From<SharedError<ConditionError>>;
-
- fn blocked(&self) -> bool;
- fn condition(&self)
- -> Shared<Box<Future<Item=(), Error=ConditionError> + Send>>;
-}
diff --git a/src/types.rs b/src/types.rs
deleted file mode 100644
index e8ae2a3..0000000
--- a/src/types.rs
+++ /dev/null
@@ -1,134 +0,0 @@
-use std::convert::AsRef;
-use std::cmp::{Eq, PartialEq};
-use std::marker::PhantomData;
-use std::convert::Into;
-use std::fmt;
-use std::fmt::{Debug, Formatter};
-
-/* Used for Id's that can be interpreted as integers but aren't returned as
- * an int by Twitch's API. (Maybe to allow a quick switch to a different representation
- * without breaking the json schema?)
- *
- * Don't Implement Display for StringID since it would allow comparisions between
- * different StringId types
- */
-
-pub struct User {}
-pub struct Video {}
-pub struct Game {}
-pub struct Clip {}
-
-pub type UserId = StringId<User>;
-pub type ChannelId = UserId;
-pub type VideoId = StringId<Video>;
-pub type ClipId = StringId<Clip>;
-pub type GameId = StringId<Game>;
-
-#[derive(Clone)]
-pub struct StringId<T> {
- id: String,
- marker: PhantomData<T>
-}
-
-impl<T> StringId<T> {
- pub fn new(id: String) -> StringId<T> {
- StringId {
- id: id,
- marker: PhantomData,
- }
- }
-}
-
-impl<'a, T> StringId<T> {
-
- pub fn from_str(id: &'a str)
- -> Result<StringId<T>, !>
- {
- Ok(StringId::new(id.to_owned()))
- }
-}
-
-impl<'a, T> AsRef<str> for StringId<T> {
- fn as_ref(&self) -> &str {
- &self.id
- }
-}
-
-
-impl<'a, T> Into<&'a str> for &'a StringId<T> {
- fn into(self) -> &'a str {
- &self.id
- }
-}
-
-impl<T> Debug for StringId<T> {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result
- {
- write!(f, "{:?}", &self.id)
- }
-}
-
-impl<T> PartialEq<StringId<T>> for StringId<T> {
- fn eq(&self, other: &StringId<T>) -> bool {
- self.id == other.id
- }
-}
-impl<T> Eq for StringId<T> {}
-
-impl<T> PartialEq<str> for StringId<T> {
- fn eq(&self, other: &str) -> bool {
- self.id.eq(other)
- }
-}
-
-use serde::{Deserialize, Deserializer};
-impl<'de, T> Deserialize<'de> for StringId<T> {
-
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where D: Deserializer<'de>
- {
- let id = String::deserialize(deserializer)?;
- Ok(StringId::new(id))
- }
-}
-
-use serde::{Serialize, Serializer};
-impl<'a, T> Serialize for StringId<T> {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer,
- {
- serializer.serialize_str(&self.id)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::{UserId, VideoId};
- use std::str::FromStr;
- #[test]
- fn test_comparison() {
- let u1 = UserId::from_str("1234");
- let u2 = UserId::from_str("1234");
-
- 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");
-
- let u2 = UserId::from_str("1235").unwrap();
- assert_ne!(u1, u2);
- assert_ne!(&u1, "1235");
-
- /* This must give a compile error */
- /*
- let v1 = VideoId::from_str("1234").unwrap();
- assert_ne!(v1, u1);
- */
- }
-
-}