use std::{
fmt,
io,
};
use serde::Deserialize;
use thiserror::Error;
pub type TwitchResult<T> = Result<T, ApiError>;
#[derive(Error, Debug)]
pub enum ApiError {
#[error("HTTP error")]
ReqwestErr(reqwest::Error),
#[error("I/O error")]
IoError(io::Error),
#[error("Serde error while parsing")]
ParseError(serde_json::error::Error),
#[error("Twitch API error")]
TwitchError(ErrorResponse),
#[error("Empty response")]
EmptyResponse(EmptyResponse),
}
impl From<reqwest::Error> for ApiError {
fn from(err: reqwest::Error) -> ApiError {
ApiError::ReqwestErr(err)
}
}
impl From<io::Error> for ApiError {
fn from(err: io::Error) -> ApiError {
ApiError::IoError(err)
}
}
impl From<serde_json::error::Error> for ApiError {
fn from(err: serde_json::error::Error) -> ApiError {
ApiError::ParseError(err)
}
}
impl From<ErrorResponse> for ApiError {
fn from(err: ErrorResponse) -> ApiError {
ApiError::TwitchError(err)
}
}
impl ApiError {
pub fn empty_response() -> ApiError {
ApiError::EmptyResponse(EmptyResponse {})
}
}
#[derive(Deserialize, Debug)]
pub struct ErrorResponse {
pub error: String,
pub status: i32,
pub message: String,
#[serde(skip_deserializing)]
pub cause: Option<Box<dyn std::error::Error>>,
}
impl fmt::Display for ErrorResponse {
fn fmt(
&self,
f: &mut fmt::Formatter,
) -> fmt::Result
{
write!(
f,
"TwitchError: (Status: {}, Error: {}, Message: {})",
&self.status, self.error, &self.message
)
}
}
#[derive(Debug)]
pub struct EmptyResponse {}
impl fmt::Display for EmptyResponse {
fn fmt(
&self,
f: &mut fmt::Formatter,
) -> fmt::Result
{
write!(f, "EmptyResponse")
}
}
macro_rules! next_result {
($obj:ident, $url:expr, $serde:ty, $lst:ident) => {{
let mut values_exist = false;
if $obj.cur.is_none() {
match $obj.client.get::<$serde>($url) {
Ok(r) => {
$obj.offset += r.$lst.len() as i32;
$obj.cur = Some(r);
values_exist = true;
}
Err(r) => writeln!(
&mut std::io::stderr(),
"{} Error: {}",
stringify!($serde),
r
)
.unwrap(),
};
}
else {
values_exist = true;
}
match values_exist {
true => {
let mut x = None;
let mut cnt = 0;
if let Some(ref mut cur) = $obj.cur {
cnt = cur.$lst.len();
if cnt > 0 {
x = Some(cur.$lst.remove(0));
cnt -= 1;
}
}
if cnt == 0 {
$obj.cur = None;
}
x
}
false => None,
}
}};
}
macro_rules! next_result_cursor {
($obj:ident, $url:expr, $serde:ty, $lst:ident) => {{
let mut values_exist = false;
if $obj.cur.is_none() {
let mut new_url = $url.clone();
{
if let Some(ref cursor) = $obj.cursor {
if cursor.len() == 0 {
return None;
}
else {
new_url.push_str("&cursor=");
new_url.push_str(cursor.clone().as_str());
}
}
}
match $obj.client.get::<$serde>(&new_url) {
Ok(r) => {
if r.$lst.len() > 0 {
values_exist = true;
}
else {
values_exist = false;
}
$obj.cursor = r._cursor.clone();
$obj.cur = Some(r);
}
Err(r) => writeln!(
&mut std::io::stderr(),
"{} Error: {}",
stringify!($serde),
r
)
.unwrap(),
};
}
else {
values_exist = true;
}
match values_exist {
true => {
let mut x = None;
let mut cnt = 0;
if let Some(ref mut cur) = $obj.cur {
cnt = cur.$lst.len();
if cnt > 0 {
x = Some(cur.$lst.remove(0));
cnt -= 1;
}
}
if cnt == 0 {
$obj.cur = None;
}
x
}
false => None,
}
}};
}