use serde::de::{self, Visitor}; use serde::{Deserialize, Deserializer}; use std::fmt; use std::io; use std::marker::PhantomData; use std::process::ExitStatus; use std::string::FromUtf8Error; use tokio::task::JoinError; use url::Url; extern crate quick_xml; extern crate reqwest; extern crate serde_json; pub type Result = std::result::Result; #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct InvidiousVideo { pub video_id: String, } #[derive(Deserialize, Debug)] pub struct VideoURL { pub url: Url, } #[derive(Deserialize, Debug)] pub struct VideoData { #[serde(default)] pub requested_formats: Vec, #[serde(default)] pub url: Option, #[serde(deserialize_with = "convert_to_u64")] pub duration: u64, pub id: String, pub title: String, #[serde(default)] pub thumbnail: Option, #[serde(default)] pub is_live: Option, #[serde(skip)] pub json: String, } #[derive(Debug)] pub struct YoutubeDLError { pub status: ExitStatus, pub stdout: String, pub stderr: String, } #[derive(Debug)] pub enum Error { IO(io::Error), JoinError(JoinError), Reqwest(reqwest::Error), QuickXML(quick_xml::Error), SerdeJSON(serde_json::Error), FromUtf8Error(FromUtf8Error), YoutubeDL(YoutubeDLError), } impl From for Error { #[inline] fn from(error: io::Error) -> Error { Error::IO(error) } } impl From for Error { #[inline] fn from(error: JoinError) -> Error { Error::JoinError(error) } } impl From for Error { #[inline] fn from(error: reqwest::Error) -> Error { Error::Reqwest(error) } } impl From for Error { #[inline] fn from(error: quick_xml::Error) -> Error { Error::QuickXML(error) } } impl From for Error { #[inline] fn from(error: serde_json::Error) -> Error { Error::SerdeJSON(error) } } impl From for Error { #[inline] fn from(error: FromUtf8Error) -> Error { Error::FromUtf8Error(error) } } fn convert_to_u64<'de, D>(deserializer: D) -> std::result::Result where D: Deserializer<'de>, { struct ConvertToU64(PhantomData T>); impl<'de> Visitor<'de> for ConvertToU64 { type Value = u64; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("an integer between 0 and 2^63") } fn visit_i8(self, value: i8) -> std::result::Result where E: de::Error, { use std::u64; if value >= 0 { Ok(value as u64) } else { Err(E::custom(format!("u64 out of range: {}", value))) } } fn visit_i16(self, value: i16) -> std::result::Result where E: de::Error, { use std::u64; if value >= 0 { Ok(value as u64) } else { Err(E::custom(format!("u64 out of range: {}", value))) } } fn visit_i32(self, value: i32) -> std::result::Result where E: de::Error, { use std::u64; if value >= 0 { Ok(value as u64) } else { Err(E::custom(format!("u64 out of range: {}", value))) } } fn visit_i64(self, value: i64) -> std::result::Result where E: de::Error, { use std::u64; if value >= 0 { Ok(value as u64) } else { Err(E::custom(format!("u64 out of range: {}", value))) } } fn visit_u8(self, value: u8) -> std::result::Result where E: de::Error, { Ok(u64::from(value)) } fn visit_u16(self, value: u16) -> std::result::Result where E: de::Error, { Ok(u64::from(value)) } fn visit_u32(self, value: u32) -> std::result::Result where E: de::Error, { Ok(u64::from(value)) } fn visit_u64(self, value: u64) -> std::result::Result where E: de::Error, { Ok(value) } fn visit_f32(self, value: f32) -> std::result::Result where E: de::Error, { let value = value.ceil(); if value >= 0.0 { Ok(value as u64) } else { Err(E::custom(format!("u64 out of range: {}", value))) } } fn visit_f64(self, value: f64) -> std::result::Result where E: de::Error, { let value = value.ceil(); if value >= 0.0 { Ok(value as u64) } else { Err(E::custom(format!("u64 out of range: {}", value))) } } } deserializer.deserialize_any(ConvertToU64(PhantomData)) }