added transmission-{remove,add}

refactored request argument object
This commit is contained in:
red 2020-04-24 22:21:47 +02:00
parent e27e3b2d04
commit d17ee2cea8
9 changed files with 194 additions and 39 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "transmission-rpc"
version = "0.1.1"
version = "0.2.0"
authors = ["red <red.avtovo@gmail.com>"]
edition = "2018"
repository = "https://github.com/j0rsa/transmission-rpc"

View File

@ -16,8 +16,8 @@ spec: https://github.com/transmission/transmission/blob/master/extras/rpc-spec.t
- [ ] torrent-set
- [X] torrent-get
- [ ] torrent-add
- [ ] torrent-remove
- [X] torrent-add
- [X] torrent-remove
- [ ] torrent-set-location
- [ ] torrent-rename-path
- [ ] session-set

25
examples/torrent-add.rs Normal file
View File

@ -0,0 +1,25 @@
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::{TorrentAddArgs, TorrentAdded};
#[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 add: TorrentAddArgs = TorrentAddArgs {
filename: Some("https://releases.ubuntu.com/20.04/ubuntu-20.04-desktop-amd64.iso.torrent".to_string()),
..TorrentAddArgs::default()
};
let res: RpcResponse<TorrentAdded> = client.torrent_add(add).await?;
println!("Add result: {:?}", &res.is_ok());
println!("response: {:?}", &res);
Ok(())
}

View File

@ -16,5 +16,6 @@ async fn main() -> Result<()> {
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(())
}

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::{Nothing};
#[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<Nothing> = client.torrent_remove(vec![1], false).await?;
println!("Remove result: {:?}", &res.is_ok());
Ok(())
}

View File

@ -15,6 +15,7 @@ use types::{Result, RpcResponse, RpcResponseArgument, RpcRequest, Nothing};
use types::SessionGet;
use types::{TorrentGetField, Torrents, Torrent};
use types::TorrentAction;
use types::{TorrentAddArgs, TorrentAdded};
pub struct TransClient {
url: String,
@ -112,7 +113,36 @@ impl TransClient {
self.call(RpcRequest::torrent_action(action, ids)).await
}
/// Performs an JRPC call to the server
/// Performs a torrent remove call
///
/// # Errors
///
/// Any IO Error or Deserialization error
///
/// # Example
///
/// in examples/torrent-remove.rs
pub async fn torrent_remove(&self, ids: Vec<i64>, delete_local_data: bool) -> Result<RpcResponse<Nothing>> {
self.call( RpcRequest::torrent_remove(ids, delete_local_data)).await
}
/// Performs a torrent add call
///
/// # Errors
///
/// Any IO Error or Deserialization error
///
/// # Example
///
/// in examples/torrent-add.rs
pub async fn torrent_add(&self, add: TorrentAddArgs) -> Result<RpcResponse<TorrentAdded>> {
if add.metainfo == None && add.filename == None {
panic!("Metainfo or Filename should be provided")
}
self.call( RpcRequest::torrent_add(add)).await
}
/// Performs a JRPC call to the server
///
/// # Errors
///
@ -142,26 +172,4 @@ impl BodyString for reqwest::RequestBuilder {
let body = rq.body().unwrap().as_bytes().unwrap();
Ok(std::str::from_utf8(body)?.to_string())
}
}
#[cfg(test)]
mod tests {
use crate::Result;
use crate::{TransClient, BasicAuth, TorrentGetField};
use crate::{RpcResponse, Torrents, Torrent};
use std::env;
use dotenv::dotenv;
#[tokio::test]
async fn it_works() -> 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.name.as_ref().unwrap()).collect();
println!("{:#?}", names);
Ok(())
}
}
}

View File

@ -14,10 +14,13 @@ pub(crate) use self::request::RpcRequest;
pub use self::request::ArgumentFields;
pub use self::request::TorrentGetField;
pub use self::request::TorrentAction;
pub use self::request::TorrentAddArgs;
pub use self::request::File;
pub use self::response::RpcResponse;
pub(crate) use self::response::RpcResponseArgument;
pub use self::response::SessionGet;
pub use self::response::Torrents;
pub use self::response::Torrent;
pub use self::response::TorrentAdded;
pub use self::response::Nothing;

View File

@ -15,38 +15,126 @@ impl RpcRequest {
}
}
pub fn torrent_get(fields: Vec<TorrentGetField>) -> RpcRequest {
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 (Args { fields: Some(string_fields), ids: None }),
arguments: Some ( Args::TorrentGetArgs(TorrentGetArgs { fields: Some(string_fields)} )),
}
}
pub fn torrent_remove(ids: Vec<i64>, delete_local_data: bool) -> RpcRequest {
RpcRequest {
method: String::from("torrent-remove"),
arguments: Some ( Args::TorrentRemoveArgs(TorrentRemoveArgs {ids, delete_local_data} ) )
}
}
pub fn torrent_add(add: TorrentAddArgs) -> RpcRequest {
RpcRequest {
method: String::from("torrent-add"),
arguments: Some ( Args::TorrentAddArgs(add) )
}
}
pub fn torrent_action(action: TorrentAction, ids: Vec<i64>) -> RpcRequest {
RpcRequest {
method: action.to_str(),
arguments: Some (Args { fields: None, ids: Some(ids) }),
arguments: Some ( Args::TorrentActionArgs(TorrentActionArgs { ids })),
}
}
}
pub trait ArgumentFields {}
impl ArgumentFields for TorrentGetField{}
#[derive(Serialize, Debug, RustcEncodable)]
struct Args {
#[derive(Serialize, Debug, RustcEncodable, Clone)]
#[serde(untagged)]
pub enum Args{
TorrentGetArgs(TorrentGetArgs),
TorrentActionArgs(TorrentActionArgs),
TorrentRemoveArgs(TorrentRemoveArgs),
TorrentAddArgs(TorrentAddArgs),
}
#[derive(Serialize, Debug, RustcEncodable, Clone)]
pub struct TorrentGetArgs {
#[serde(skip_serializing_if="Option::is_none")]
fields: Option<Vec<String>>,
fields: Option<Vec<String>>
}
#[derive(Serialize, Debug, RustcEncodable, Clone)]
pub struct TorrentActionArgs {
ids: Vec<i64>,
}
#[derive(Serialize, Debug, RustcEncodable, Clone)]
pub struct TorrentRemoveArgs {
ids: Vec<i64>,
#[serde(rename="delete-local-data")]
delete_local_data: bool
}
#[derive(Serialize, Debug, RustcEncodable, Clone)]
pub struct TorrentAddArgs {
#[serde(skip_serializing_if="Option::is_none")]
ids: Option<Vec<i64>>
pub cookies: Option<String>,
#[serde(skip_serializing_if="Option::is_none", rename="download-dir")]
pub download_dir: Option<String>,
/// Either "filename" OR "metainfo" MUST be included
/// semi-optional
/// filename or URL of the .torrent file
#[serde(skip_serializing_if="Option::is_none")]
pub filename: Option<String>,
/// semi-optional
/// base64-encoded .torrent content
#[serde(skip_serializing_if="Option::is_none")]
pub metainfo: Option<String>,
#[serde(skip_serializing_if="Option::is_none")]
pub paused: Option<bool>,
#[serde(skip_serializing_if="Option::is_none", rename="peer-limit")]
pub peer_limit: Option<i64>,
#[serde(skip_serializing_if="Option::is_none", rename="bandwidthPriority")]
pub bandwidth_priority: Option<i64>,
#[serde(skip_serializing_if="Option::is_none", rename="files-wanted")]
pub files_wanted: Option<Vec<File>>,
#[serde(skip_serializing_if="Option::is_none", rename="files-unwanted")]
pub files_unwanted: Option<Vec<File>>,
#[serde(skip_serializing_if="Option::is_none", rename="priority-high")]
pub priority_high: Option<Vec<File>>,
#[serde(skip_serializing_if="Option::is_none", rename="priority-low")]
pub priority_low: Option<Vec<File>>,
#[serde(skip_serializing_if="Option::is_none", rename="priority-normal")]
pub priority_normal: Option<Vec<File>>,
}
impl Default for TorrentAddArgs {
fn default() -> Self {
TorrentAddArgs {
cookies: None,
download_dir: None,
filename: None,
metainfo: None,
paused: None,
peer_limit: None,
bandwidth_priority: None,
files_wanted: None,
files_unwanted: None,
priority_high: None,
priority_low: None,
priority_normal: None
}
}
}
#[derive(Serialize, Debug, RustcEncodable, Clone)]
pub struct File {
//todo
}
pub enum TorrentGetField {
Id,
Addeddate,
Name,
HashString,
Totalsize,
Error,
Errorstring,
@ -80,6 +168,7 @@ impl TorrentGetField {
TorrentGetField::Id => "id",
TorrentGetField::Addeddate => "addedDate",
TorrentGetField::Name => "name",
TorrentGetField::HashString => "hashString",
TorrentGetField::Totalsize => "totalSize",
TorrentGetField::Error => "error",
TorrentGetField::Errorstring => "errorString",

View File

@ -27,7 +27,7 @@ pub struct SessionGet {
}
impl RpcResponseArgument for SessionGet{}
#[derive(Deserialize, Debug)]
#[derive(Deserialize, Debug, RustcEncodable)]
pub struct Torrent {
#[serde(rename="addedDate")]
pub added_date: Option<i64>,
@ -47,6 +47,8 @@ pub struct Torrent {
#[serde(rename="metadataPercentComplete")]
pub metadata_percent_complete: Option<f32>,
pub name: Option<String>,
#[serde(rename="hashString")]
pub hash_string: Option<String>,
#[serde(rename="peersConnected")]
pub peers_connected: Option<i64>,
#[serde(rename="peersGettingFromUs")]
@ -74,12 +76,12 @@ pub struct Torrent {
#[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>
}
impl RpcResponseArgument for Torrents<Torrent>{}
#[derive(Deserialize, Debug, RustcEncodable)]
pub struct Trackers {
@ -89,4 +91,11 @@ pub struct Trackers {
#[derive(Deserialize, Debug, RustcEncodable)]
pub struct Nothing{}
impl RpcResponseArgument for Nothing {}
impl RpcResponseArgument for Nothing {}
#[derive(Deserialize, Debug, RustcEncodable)]
pub struct TorrentAdded {
#[serde(rename="torrent-added")]
pub torrent_added: Torrent
}
impl RpcResponseArgument for TorrentAdded{}