diff --git a/README.md b/README.md index 00fa651..01e75e5 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ spec: https://github.com/transmission/transmission/blob/master/extras/rpc-spec.t Supported Methods: - [ ] torrent-set -- [ ] torrent-get +- [X] torrent-get - [ ] torrent-add - [ ] torrent-remove - [ ] torrent-set-location diff --git a/examples/get_session.rs b/examples/session-get.rs similarity index 69% rename from examples/get_session.rs rename to examples/session-get.rs index f948795..f396538 100644 --- a/examples/get_session.rs +++ b/examples/session-get.rs @@ -3,7 +3,7 @@ extern crate transmission_rpc; use std::env; use dotenv::dotenv; use transmission_rpc::TransClient; -use transmission_rpc::types::{Result, RpcResponse, SessionInfo, BasicAuth}; +use transmission_rpc::types::{Result, RpcResponse, SessionGet, BasicAuth}; #[tokio::main] async fn main() -> Result<()> { @@ -12,10 +12,11 @@ async fn main() -> Result<()> { let url= env::var("TURL")?; let basic_auth = BasicAuth{user: env::var("TUSER")?, password: env::var("TPWD")?}; let client = TransClient::with_auth(&url, basic_auth); - let response: Result> = client.session_get().await; + let response: Result> = client.session_get().await; match response { Ok(_) => println!("Yay!"), Err(_) => panic!("Oh no!") } + println!("Rpc reqsponse is ok: {}", response?.is_ok()); Ok(()) } \ No newline at end of file diff --git a/examples/torrent-get.rs b/examples/torrent-get.rs new file mode 100644 index 0000000..eff5d58 --- /dev/null +++ b/examples/torrent-get.rs @@ -0,0 +1,20 @@ +extern crate transmission_rpc; + +use std::env; +use dotenv::dotenv; +use transmission_rpc::TransClient; +use transmission_rpc::types::{Result, RpcResponse, BasicAuth}; +use transmission_rpc::types::{Torrents, Torrent, TorrentGetField}; + +#[tokio::main] +async fn main() -> Result<()> { + dotenv().ok(); + env_logger::init(); + let url= env::var("TURL")?; + let basic_auth = BasicAuth{user: env::var("TUSER")?, password: env::var("TPWD")?}; + let client = TransClient::with_auth(&url, basic_auth); + let res: RpcResponse> = client.torrent_get(vec![TorrentGetField::ID, TorrentGetField::NAME]).await?; + let names: Vec<&String> = res.arguments.torrents.iter().map(|it| it.clone().name.as_ref().unwrap()).collect(); + println!("{:#?}", names); + Ok(()) +} \ No newline at end of file diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000..4cc4dad Binary files /dev/null and b/src/.DS_Store differ diff --git a/src/lib.rs b/src/lib.rs index 2e42c3e..cae5b15 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,13 +10,14 @@ extern crate log; extern crate reqwest; extern crate tokio_postgres; -use serde::Serialize; use serde::de::DeserializeOwned; use reqwest::header::CONTENT_TYPE; pub mod types; -use types::{Result, RpcRequestArgument, RpcResponse, RpcResponseArgument, SessionGet, SessionInfo}; use types::BasicAuth; +use types::{Result, RpcResponse, RpcResponseArgument, RpcRequest}; +use types::SessionGet; +use types::{TorrentGetField, Torrents, Torrent}; pub struct TransClient { url: String, @@ -52,7 +53,7 @@ impl TransClient { async fn get_session_id(&self) -> String { info!("Requesting session id info"); let response: reqwest::Response = self.rpc_request() - .json(&SessionGet::new()) + .json(&RpcRequest::session_get()) .send() .await .unwrap(); @@ -67,15 +68,16 @@ impl TransClient { } - pub async fn session_get(&self) -> Result> { - self.call(SessionGet::new()).await + pub async fn session_get(&self) -> Result> { + self.call(RpcRequest::session_get()).await } - pub async fn torrent_get(&self) -> Result>{} + pub async fn torrent_get(&self, fields: Vec) -> Result>> { + self.call(RpcRequest::torrent_get(fields)).await + } - async fn call (&self, request: T) -> Result> - where T : RpcRequestArgument + Serialize, - U : RpcResponseArgument + DeserializeOwned + std::fmt::Debug + async fn call (&self, request: RpcRequest) -> Result> + where RS : RpcResponseArgument + DeserializeOwned + std::fmt::Debug { info!("Loaded auth: {:?}", &self.auth); let rq: reqwest::RequestBuilder = self.rpc_request() @@ -83,9 +85,8 @@ impl TransClient { .json(&request); info!("Request body: {:?}", rq.try_clone().unwrap().body_string()?); let resp: reqwest::Response = rq.send().await?; - // print!("{:?}", resp.text().await); - let rpc_response: RpcResponse = resp.json().await?; - info!("{:#?}", rpc_response); + let rpc_response: RpcResponse = resp.json().await?; + info!("Response body: {:#?}", rpc_response); Ok(rpc_response) } } @@ -105,7 +106,8 @@ impl BodyString for reqwest::RequestBuilder { #[cfg(test)] mod tests { use crate::Result; - use crate::{TransClient, BasicAuth}; + use crate::{TransClient, BasicAuth, TorrentGetField}; + use crate::{RpcResponse, Torrents, Torrent}; use std::env; use dotenv::dotenv; @@ -115,7 +117,10 @@ mod tests { env_logger::init(); let url= env::var("TURL")?; let basic_auth = BasicAuth{user: env::var("TUSER")?, password: env::var("TPWD")?}; - TransClient::with_auth(&url, basic_auth).session_get().await; + let client = TransClient::with_auth(&url, basic_auth); + let res: RpcResponse> = client.torrent_get(vec![TorrentGetField::ID, TorrentGetField::NAME]).await?; + let names: Vec<&String> = res.arguments.torrents.iter().map(|it| it.name.as_ref().unwrap()).collect(); + println!("{:#?}", names); Ok(()) } } diff --git a/src/types/mod.rs b/src/types/mod.rs index 20f05f7..1a7a2ba 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -10,10 +10,12 @@ pub struct BasicAuth { pub password: String, } -pub(crate) use self::request::RpcRequestArgument; -pub(crate) use self::request::SessionGet; +pub(crate) use self::request::RpcRequest; +pub use self::request::ArgumentFields; +pub use self::request::TorrentGetField; pub use self::response::RpcResponse; - pub(crate) use self::response::RpcResponseArgument; -pub use self::response::SessionInfo; \ No newline at end of file +pub use self::response::SessionGet; +pub use self::response::Torrents; +pub use self::response::Torrent; \ No newline at end of file diff --git a/src/types/request.rs b/src/types/request.rs index 022d2a7..3a608ff 100644 --- a/src/types/request.rs +++ b/src/types/request.rs @@ -1,16 +1,105 @@ use serde::Serialize; #[derive(Serialize, Debug, RustcEncodable)] -pub struct SessionGet { - method: String +pub struct RpcRequest { + method: String, + #[serde(skip_serializing_if="Option::is_none")] + arguments: Option>, + #[serde(skip_serializing_if="Option::is_none")] + ids: Option, } -impl SessionGet{ - pub fn new() -> SessionGet { - SessionGet { method: String::from("session-get") } +impl RpcRequest { + pub fn session_get() -> RpcRequest { + RpcRequest { + method: String::from("session-get"), + arguments: None, + ids: None, + } } + + pub fn torrent_get(fields: Vec) -> RpcRequest { + let string_fields = fields.iter().map(|f| f.to_str()).collect(); + RpcRequest { + method: String::from("torrent-get"), + arguments: Some (Fields { fields: string_fields }), + ids: None, + } + } + } -pub trait RpcRequestArgument {} -impl RpcRequestArgument for SessionGet{} \ No newline at end of file +pub trait ArgumentFields {} +impl ArgumentFields for TorrentGetField{} + +#[derive(Serialize, Debug, RustcEncodable)] +struct Fields { + fields: Vec +} + +pub enum TorrentGetField { + ID, + ADDEDDATE, + NAME, + TOTALSIZE, + ERROR, + ERRORSTRING, + ETA, + ISFINISHED, + ISSTALLED, + LEFTUNTILDONE, + METADATAPERCENTCOMPLETE, + PEERSCONNECTED, + PEERSGETTINGFROMUS, + PEERSSENDINGTOUS, + PERCENTDONE, + QUEUEPOSITION, + RATEDOWNLOAD, + RATEUPLOAD, + RECHECKPROGRESS, + SEEDRATIOMODE, + SEEDRATIOLIMIT, + SIZEWHENDONE, + STATUS, + TRACKERS, + DOWNLOADDIR, + UPLOADEDEVER, + UPLOADRATIO, + WEBSEEDSSENDINGTOUS, +} + +impl TorrentGetField { + pub fn to_str(&self) -> String { + match self { + TorrentGetField::ID => "id", + TorrentGetField::ADDEDDATE => "addedDate", + TorrentGetField::NAME => "name", + TorrentGetField::TOTALSIZE => "totalSize", + TorrentGetField::ERROR => "error", + TorrentGetField::ERRORSTRING => "errorString", + TorrentGetField::ETA => "eta", + TorrentGetField::ISFINISHED => "isFinished", + TorrentGetField::ISSTALLED => "isStalled", + TorrentGetField::LEFTUNTILDONE => "leftUntilDone", + TorrentGetField::METADATAPERCENTCOMPLETE => "metadataPercentComplete", + TorrentGetField::PEERSCONNECTED => "peersConnected", + TorrentGetField::PEERSGETTINGFROMUS => "peersGettingFromUs", + TorrentGetField::PEERSSENDINGTOUS => "peersSendingToUs", + TorrentGetField::PERCENTDONE => "percentDone", + TorrentGetField::QUEUEPOSITION => "queuePosition", + TorrentGetField::RATEDOWNLOAD => "rateDownload", + TorrentGetField::RATEUPLOAD => "rateUpload", + TorrentGetField::RECHECKPROGRESS => "recheckProgress", + TorrentGetField::SEEDRATIOMODE => "seedRatioMode", + TorrentGetField::SEEDRATIOLIMIT => "seedRatioLimit", + TorrentGetField::SIZEWHENDONE => "sizeWhenDone", + TorrentGetField::STATUS => "status", + TorrentGetField::TRACKERS => "trackers", + TorrentGetField::DOWNLOADDIR => "downloadDir", + TorrentGetField::UPLOADEDEVER => "uploadedEver", + TorrentGetField::UPLOADRATIO => "uploadRatio", + TorrentGetField::WEBSEEDSSENDINGTOUS => "webseedsSendingToUs", + }.to_string() + } +} \ No newline at end of file diff --git a/src/types/response.rs b/src/types/response.rs index 37ff59e..2a6a0e1 100644 --- a/src/types/response.rs +++ b/src/types/response.rs @@ -1,23 +1,81 @@ use serde::Deserialize; #[derive(Deserialize, Debug)] -pub struct RpcResponse { - arguments: T, - result: String +pub struct RpcResponse { + pub arguments: T, + pub result: String } +impl RpcResponse { + pub fn is_ok(&self) -> bool { + self.result == "success" + } +} +pub trait RpcResponseArgument {} + #[derive(Deserialize, Debug)] -pub struct SessionInfo { +pub struct SessionGet { #[serde(rename="blocklist-enabled")] - blocklist_enabled: bool, + pub blocklist_enabled: bool, #[serde(rename="download-dir")] - download_dir: String, - encryption: String, + pub download_dir: String, + pub encryption: String, #[serde(rename="rpc-version")] - rpc_version: i32, + pub rpc_version: i32, #[serde(rename="rpc-version-minimum")] - rpc_version_minimum: i32, - version: String, + pub rpc_version_minimum: i32, + pub version: String, } +impl RpcResponseArgument for SessionGet{} -pub trait RpcResponseArgument {} -impl RpcResponseArgument for SessionInfo{} \ No newline at end of file +#[derive(Deserialize, Debug)] +pub struct Torrent { + #[serde(rename="addedDate")] + pub added_date: Option, + #[serde(rename="downloadDir")] + pub download_dir: Option, + pub error: Option, + #[serde(rename="errorString")] + pub error_string: Option, + pub eta: Option, + pub id: Option, + #[serde(rename="isFinished")] + pub is_finished: Option, + #[serde(rename="isStalled")] + pub is_stalled: Option, + #[serde(rename="leftUntilDone")] + pub left_until_done: Option, + #[serde(rename="metadataPercentComplete")] + pub metadata_percent_complete: Option, + pub name: Option, + #[serde(rename="peersConnected")] + pub peers_connected: Option, + #[serde(rename="peersGettingFromUs")] + pub peers_getting_from_us: Option, + #[serde(rename="peersSendingToUs")] + pub peers_sending_to_us: Option, + #[serde(rename="percentDone")] + pub percent_done: Option, + #[serde(rename="rateDownload")] + pub rate_download: Option, + #[serde(rename="rateUpload")] + pub rate_upload: Option, + #[serde(rename="recheckProgress")] + pub recheck_progress: Option, + #[serde(rename="seedRatioLimit")] + pub seed_ratio_limit: Option, + #[serde(rename="sizeWhenDone")] + pub size_when_done: Option, + pub status: Option, + #[serde(rename="totalSize")] + pub total_size: Option, + #[serde(rename="uploadRatio")] + pub upload_ratio: Option, + #[serde(rename="uploadedEver")] + pub uploaded_ever: Option, +} +impl RpcResponseArgument for Torrents{} + +#[derive(Deserialize, Debug, RustcEncodable)] +pub struct Torrents { + pub torrents: Vec +} \ No newline at end of file