diff --git a/src/async_api.rs b/src/async_api.rs new file mode 100644 index 0000000..c5a61fb --- /dev/null +++ b/src/async_api.rs @@ -0,0 +1,105 @@ +use snafu::ResultExt; +use qbittorrent_web_api::Api; +use qbittorrent_web_api::api_impl::Error as QBittorrentError; + +use crate::config::Config; +use crate::error::{Error, InternalApiSnafu as ApiError}; + +#[derive(Debug)] +pub struct UnauthenticatedRawApiClient; + +impl UnauthenticatedRawApiClient { + /// Initialize a blocking runtime for the API client + pub fn new() -> UnauthenticatedRawApiClient { + UnauthenticatedRawApiClient + } + + /// Login into a qBittorrent backend and return a proper ApiClient instance + pub async fn login(self, host: &str, login: &str, password: &str) -> Result { + let api = Api::login(host, login, password).await?; + Ok(RawApiClient { + api, + }) + } +} + +#[derive(Debug)] +pub struct RawApiClient { + api: qbittorrent_web_api::api_impl::Authenticated, +} + +impl RawApiClient { + pub async fn add(&self, magnet: &str, paused: bool) -> Result { + let base_call = self.api.torrent_management(); + let call = if paused { + base_call.add(magnet).paused("true") + } else { + base_call.add(magnet) + }; + call.send_raw().await + } + + pub async fn get(&self, hash: &str) -> Result { + self.api.torrent_management().properties_raw(hash).await + } + + pub async fn list(&self) -> Result { + self.api.torrent_management().info().send_raw().await + } + +} + +#[derive(Debug)] +/// ApiClient is a convenience struct around qbittorrent_web_api for use in async programs, using qbt::Error error types. +pub struct ApiClient { + pub raw_api: RawApiClient, +} + +impl ApiClient { + /// Login to a qBittorrent backend and return a handle to the ApiClient + pub async fn new(host: &str, login: &str, password: &str) -> Result { + let unauthenticated = UnauthenticatedRawApiClient::new(); + let authenticated = unauthenticated.login(host, login, password).await.map_err(|e| { + match e { + QBittorrentError::HttpError(_) => { + Error::FailedToReachAPI { source: e } + }, QBittorrentError::InvalidUsernameOrPassword => { + Error::FailedLogin { user: login.to_string() } + } _ => { + panic!("Cookie error"); + } + } + })?; + Ok(ApiClient { + raw_api: authenticated, + }) + } + + pub async fn from_config(config: &Config) -> Result { + Self::new(&config.format_host(), &config.qbittorrent.login, &config.qbittorrent.password).await + } + + + pub async fn add(&self, magnet: &str, paused: bool) -> Result<(), Error> { + let res = self.raw_api.add(magnet, paused).await.context(ApiError)?; + if res == "Ok." { + Ok(()) + } else { + Err(Error::message(res.clone())) + } + } + + pub async fn get(&self, hash: &str) -> Result, Error> { + let res = self.raw_api.get(hash).await.context(ApiError)?; + if res == "" { + Ok(None) + } else { + Ok(Some(res)) + } + } + + pub async fn list(&self) -> Result { + self.raw_api.list().await.context(ApiError) + } + +} diff --git a/src/lib.rs b/src/lib.rs index 73cb595..df9f167 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ pub mod action; pub mod api; +pub mod async_api; pub mod config; pub use config::Config; pub mod cli;