added torrent-get

This commit is contained in:
red 2020-04-21 11:23:22 +02:00
parent 4a9077b95f
commit 0083488ddb
8 changed files with 215 additions and 40 deletions

View File

@ -5,7 +5,7 @@ spec: https://github.com/transmission/transmission/blob/master/extras/rpc-spec.t
Supported Methods: Supported Methods:
- [ ] torrent-set - [ ] torrent-set
- [ ] torrent-get - [X] torrent-get
- [ ] torrent-add - [ ] torrent-add
- [ ] torrent-remove - [ ] torrent-remove
- [ ] torrent-set-location - [ ] torrent-set-location

View File

@ -3,7 +3,7 @@ extern crate transmission_rpc;
use std::env; use std::env;
use dotenv::dotenv; use dotenv::dotenv;
use transmission_rpc::TransClient; use transmission_rpc::TransClient;
use transmission_rpc::types::{Result, RpcResponse, SessionInfo, BasicAuth}; use transmission_rpc::types::{Result, RpcResponse, SessionGet, BasicAuth};
#[tokio::main] #[tokio::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {
@ -12,10 +12,11 @@ async fn main() -> Result<()> {
let url= env::var("TURL")?; let url= env::var("TURL")?;
let basic_auth = BasicAuth{user: env::var("TUSER")?, password: env::var("TPWD")?}; let basic_auth = BasicAuth{user: env::var("TUSER")?, password: env::var("TPWD")?};
let client = TransClient::with_auth(&url, basic_auth); let client = TransClient::with_auth(&url, basic_auth);
let response: Result<RpcResponse<SessionInfo>> = client.session_get().await; let response: Result<RpcResponse<SessionGet>> = client.session_get().await;
match response { match response {
Ok(_) => println!("Yay!"), Ok(_) => println!("Yay!"),
Err(_) => panic!("Oh no!") Err(_) => panic!("Oh no!")
} }
println!("Rpc reqsponse is ok: {}", response?.is_ok());
Ok(()) Ok(())
} }

20
examples/torrent-get.rs Normal file
View File

@ -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<Torrents<Torrent>> = 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(())
}

BIN
src/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -10,13 +10,14 @@ extern crate log;
extern crate reqwest; extern crate reqwest;
extern crate tokio_postgres; extern crate tokio_postgres;
use serde::Serialize;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use reqwest::header::CONTENT_TYPE; use reqwest::header::CONTENT_TYPE;
pub mod types; pub mod types;
use types::{Result, RpcRequestArgument, RpcResponse, RpcResponseArgument, SessionGet, SessionInfo};
use types::BasicAuth; use types::BasicAuth;
use types::{Result, RpcResponse, RpcResponseArgument, RpcRequest};
use types::SessionGet;
use types::{TorrentGetField, Torrents, Torrent};
pub struct TransClient { pub struct TransClient {
url: String, url: String,
@ -52,7 +53,7 @@ impl TransClient {
async fn get_session_id(&self) -> String { async fn get_session_id(&self) -> String {
info!("Requesting session id info"); info!("Requesting session id info");
let response: reqwest::Response = self.rpc_request() let response: reqwest::Response = self.rpc_request()
.json(&SessionGet::new()) .json(&RpcRequest::session_get())
.send() .send()
.await .await
.unwrap(); .unwrap();
@ -67,15 +68,16 @@ impl TransClient {
} }
pub async fn session_get(&self) -> Result<RpcResponse<SessionInfo>> { pub async fn session_get(&self) -> Result<RpcResponse<SessionGet>> {
self.call(SessionGet::new()).await self.call(RpcRequest::session_get()).await
} }
pub async fn torrent_get(&self) -> Result<RpcResponse<>>{} pub async fn torrent_get(&self, fields: Vec<TorrentGetField>) -> Result<RpcResponse<Torrents<Torrent>>> {
self.call(RpcRequest::torrent_get(fields)).await
}
async fn call<T, U> (&self, request: T) -> Result<RpcResponse<U>> async fn call<RS> (&self, request: RpcRequest) -> Result<RpcResponse<RS>>
where T : RpcRequestArgument + Serialize, where RS : RpcResponseArgument + DeserializeOwned + std::fmt::Debug
U : RpcResponseArgument + DeserializeOwned + std::fmt::Debug
{ {
info!("Loaded auth: {:?}", &self.auth); info!("Loaded auth: {:?}", &self.auth);
let rq: reqwest::RequestBuilder = self.rpc_request() let rq: reqwest::RequestBuilder = self.rpc_request()
@ -83,9 +85,8 @@ impl TransClient {
.json(&request); .json(&request);
info!("Request body: {:?}", rq.try_clone().unwrap().body_string()?); info!("Request body: {:?}", rq.try_clone().unwrap().body_string()?);
let resp: reqwest::Response = rq.send().await?; let resp: reqwest::Response = rq.send().await?;
// print!("{:?}", resp.text().await); let rpc_response: RpcResponse<RS> = resp.json().await?;
let rpc_response: RpcResponse<U> = resp.json().await?; info!("Response body: {:#?}", rpc_response);
info!("{:#?}", rpc_response);
Ok(rpc_response) Ok(rpc_response)
} }
} }
@ -105,7 +106,8 @@ impl BodyString for reqwest::RequestBuilder {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::Result; use crate::Result;
use crate::{TransClient, BasicAuth}; use crate::{TransClient, BasicAuth, TorrentGetField};
use crate::{RpcResponse, Torrents, Torrent};
use std::env; use std::env;
use dotenv::dotenv; use dotenv::dotenv;
@ -115,7 +117,10 @@ mod tests {
env_logger::init(); env_logger::init();
let url= env::var("TURL")?; let url= env::var("TURL")?;
let basic_auth = BasicAuth{user: env::var("TUSER")?, password: env::var("TPWD")?}; 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<Torrents<Torrent>> = 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(()) Ok(())
} }
} }

View File

@ -10,10 +10,12 @@ pub struct BasicAuth {
pub password: String, pub password: String,
} }
pub(crate) use self::request::RpcRequestArgument; pub(crate) use self::request::RpcRequest;
pub(crate) use self::request::SessionGet; pub use self::request::ArgumentFields;
pub use self::request::TorrentGetField;
pub use self::response::RpcResponse; pub use self::response::RpcResponse;
pub(crate) use self::response::RpcResponseArgument; pub(crate) use self::response::RpcResponseArgument;
pub use self::response::SessionInfo; pub use self::response::SessionGet;
pub use self::response::Torrents;
pub use self::response::Torrent;

View File

@ -1,16 +1,105 @@
use serde::Serialize; use serde::Serialize;
#[derive(Serialize, Debug, RustcEncodable)] #[derive(Serialize, Debug, RustcEncodable)]
pub struct SessionGet { pub struct RpcRequest {
method: String method: String,
#[serde(skip_serializing_if="Option::is_none")]
arguments: Option<Fields<String>>,
#[serde(skip_serializing_if="Option::is_none")]
ids: Option<String>,
} }
impl SessionGet{ impl RpcRequest {
pub fn new() -> SessionGet { pub fn session_get() -> RpcRequest {
SessionGet { method: String::from("session-get") } RpcRequest {
method: String::from("session-get"),
arguments: None,
ids: None,
}
} }
pub fn torrent_get(fields: Vec<TorrentGetField>) -> 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 {} pub trait ArgumentFields {}
impl RpcRequestArgument for SessionGet{} impl ArgumentFields for TorrentGetField{}
#[derive(Serialize, Debug, RustcEncodable)]
struct Fields<T> {
fields: Vec<T>
}
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()
}
}

View File

@ -1,23 +1,81 @@
use serde::Deserialize; use serde::Deserialize;
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct RpcResponse<T> { pub struct RpcResponse<T: RpcResponseArgument> {
arguments: T, pub arguments: T,
result: String pub result: String
} }
impl<T: RpcResponseArgument> RpcResponse<T> {
pub fn is_ok(&self) -> bool {
self.result == "success"
}
}
pub trait RpcResponseArgument {}
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct SessionInfo { pub struct SessionGet {
#[serde(rename="blocklist-enabled")] #[serde(rename="blocklist-enabled")]
blocklist_enabled: bool, pub blocklist_enabled: bool,
#[serde(rename="download-dir")] #[serde(rename="download-dir")]
download_dir: String, pub download_dir: String,
encryption: String, pub encryption: String,
#[serde(rename="rpc-version")] #[serde(rename="rpc-version")]
rpc_version: i32, pub rpc_version: i32,
#[serde(rename="rpc-version-minimum")] #[serde(rename="rpc-version-minimum")]
rpc_version_minimum: i32, pub rpc_version_minimum: i32,
version: String, pub version: String,
} }
impl RpcResponseArgument for SessionGet{}
pub trait RpcResponseArgument {} #[derive(Deserialize, Debug)]
impl RpcResponseArgument for SessionInfo{} pub struct Torrent {
#[serde(rename="addedDate")]
pub added_date: Option<i64>,
#[serde(rename="downloadDir")]
pub download_dir: Option<String>,
pub error: Option<i64>,
#[serde(rename="errorString")]
pub error_string: Option<String>,
pub eta: Option<i64>,
pub id: Option<i64>,
#[serde(rename="isFinished")]
pub is_finished: Option<bool>,
#[serde(rename="isStalled")]
pub is_stalled: Option<bool>,
#[serde(rename="leftUntilDone")]
pub left_until_done: Option<i64>,
#[serde(rename="metadataPercentComplete")]
pub metadata_percent_complete: Option<f32>,
pub name: Option<String>,
#[serde(rename="peersConnected")]
pub peers_connected: Option<i64>,
#[serde(rename="peersGettingFromUs")]
pub peers_getting_from_us: Option<i64>,
#[serde(rename="peersSendingToUs")]
pub peers_sending_to_us: Option<i64>,
#[serde(rename="percentDone")]
pub percent_done: Option<f32>,
#[serde(rename="rateDownload")]
pub rate_download: Option<i64>,
#[serde(rename="rateUpload")]
pub rate_upload: Option<i64>,
#[serde(rename="recheckProgress")]
pub recheck_progress: Option<f32>,
#[serde(rename="seedRatioLimit")]
pub seed_ratio_limit: Option<f32>,
#[serde(rename="sizeWhenDone")]
pub size_when_done: Option<i64>,
pub status: Option<i64>,
#[serde(rename="totalSize")]
pub total_size: Option<i64>,
#[serde(rename="uploadRatio")]
pub upload_ratio: Option<f32>,
#[serde(rename="uploadedEver")]
pub uploaded_ever: Option<i64>,
}
impl RpcResponseArgument for Torrents<Torrent>{}
#[derive(Deserialize, Debug, RustcEncodable)]
pub struct Torrents<T> {
pub torrents: Vec<T>
}