use std::convert::TryInto;
use std::error::Error;
use serde::Serialize;
mod payload;
use payload::Request;
use payload::Response;
mod list;
use list::FieldList;
use list::TorrentList;
mod torrent;
use torrent::Torrent;
use torrent::AddTorrent;
use torrent::AddTorrentResponse;
#[derive(Clone)]
pub struct Client {
host: String,
port: u16,
tls: bool,
auth: Option<(String, String)>,
base_url: String,
session_id: Option<String>,
http: reqwest::Client,
}
impl Client {
pub fn new(host: impl ToString, port: u16, tls: bool, auth: Option<(String, String)>) -> Result<Self, Box<dyn Error>> {
let mut headers = reqwest::header::HeaderMap::new();
headers.insert(reqwest::header::USER_AGENT, "transmission-rs/0.1".try_into()?);
let this = Self{
host: host.to_string(),
port: port,
tls: tls,
auth: auth,
base_url: {
let protocol = match tls {
true => "https",
false => "http"
};
format!("{}://{}:{}", protocol, host.to_string(), port)
},
session_id: None,
http: reqwest::Client::builder()
.gzip(true)
.default_headers(headers)
.build()?
};
Ok(this)
}
fn build_url(&self, path: impl AsRef<str>) -> String {
format!("{}{}", self.base_url, path.as_ref()).to_string()
}
pub async fn authenticate(&mut self) -> Result<(), Box<dyn Error>> {
let response = self.post("/transmission/rpc/")
.header("Content-Type", "application/x-www-form-urlencoded")
.send()
.await?;
self.session_id = match response.headers().get("X-Transmission-Session-Id") {
Some(v) => Some(v.to_str()?.to_string()),
None => None };
Ok(())
}
pub fn post(&self, path: impl AsRef<str>) -> reqwest::RequestBuilder {
let url = self.build_url(path);
let mut request = self.http.post(&url);
request = match &self.auth {
Some(auth) => request.basic_auth(&auth.0, Some(&auth.1)),
None => request
};
request = match &self.session_id {
Some(token) => request.header("X-Transmission-Session-Id", token),
None => request
};
request
}
pub async fn rpc(&self, method: impl ToString, tag: u8, body: impl Serialize) -> Result<reqwest::Response, Box<dyn Error>> {
let request = Request::new(method, tag, body);
Ok(self.post("/transmission/rpc/")
.header("Content-Type", "application/x-www-form-urlencoded")
.body(serde_json::to_string(&request)?)
.send().await?
.error_for_status()?
)
}
pub async fn list(&self) -> Result<Vec<Torrent>, Box<dyn Error>> {
let response: Response<TorrentList> = self.rpc("torrent-get", 4, FieldList::new()).await?.error_for_status()?.json().await?;
Ok(response.arguments.torrents)
}
pub async fn add_torrent_from_link(&self, url: impl ToString) -> Result<AddTorrentResponse, Box<dyn Error>> {
let response: Response<AddTorrentResponse> = self.rpc("torrent-add", 8, AddTorrent{filename: url.to_string()}).await?.error_for_status()?.json().await?;
Ok(response.arguments)
}
}