use std::ffi;
use std::mem;
use std::path::PathBuf;
use std::ptr::{null, null_mut, NonNull};
use std::sync::{Arc, RwLock};
use serde::ser::{SerializeStruct, Serializer};
use serde::{Deserialize, Serialize};
use transmission_sys;
use super::torrentinfo::TorrentFile;
use super::TorrentBuilder;
use super::TorrentInfo;
use super::TorrentStats;
use crate::error::{Error, ParseInt, TrResult};
#[derive(Debug, Serialize, Deserialize)]
#[repr(i8)]
pub enum Priority {
Low = transmission_sys::TR_PRI_LOW as i8,
Normal = transmission_sys::TR_PRI_NORMAL as i8,
High = transmission_sys::TR_PRI_HIGH as i8,
}
impl From<transmission_sys::_bindgen_ty_2> for Priority {
fn from(f: transmission_sys::_bindgen_ty_2) -> Self {
match f {
transmission_sys::TR_PRI_LOW => Priority::Low,
transmission_sys::TR_PRI_NORMAL => Priority::Normal,
transmission_sys::TR_PRI_HIGH => Priority::High,
}
}
}
impl From<i8> for Priority {
fn from(f: i8) -> Self {
match f {
x if x < 0 => Priority::Low,
0 => Priority::Normal,
x if x < 0 => Priority::High,
_ => Priority::Normal,
}
}
}
#[derive(Clone)]
pub struct Torrent {
tr_torrent: Arc<RwLock<NonNull<transmission_sys::tr_torrent>>>,
}
impl<'a> Torrent {
pub(crate) fn from_ctor(ctor: *mut transmission_sys::tr_ctor) -> TrResult<Self> {
let tor;
let mut error = 0;
let mut dupli = 0;
unsafe {
tor = transmission_sys::tr_torrentNew(ctor, &mut error, &mut dupli);
}
Error::from(error as ParseInt).to_result().and_then(|_| {
Ok(Self {
tr_torrent: Arc::new(RwLock::new(NonNull::new(tor).unwrap())),
})
})
}
pub(crate) fn from_tr_torrent(tr_torrent: *mut transmission_sys::tr_torrent) -> TrResult<Self> {
Ok(Self {
tr_torrent: Arc::new(RwLock::new(NonNull::new(tr_torrent).unwrap())),
})
}
pub fn parse_torrent_file(path: &str) -> TrResult<TorrentInfo> {
let path = ffi::CString::new(path).unwrap();
unsafe {
let ctor = transmission_sys::tr_ctorNew(null());
let mut info: transmission_sys::tr_info = mem::uninitialized();
match transmission_sys::tr_ctorSetMetainfoFromFile(ctor, path.as_ptr()) {
0 => match transmission_sys::tr_torrentParse(ctor, &mut info) {
transmission_sys::tr_parse_result::TR_PARSE_OK => Ok(TorrentInfo::from(info)),
_ => Err(Error::ParseErr),
},
_ => Err(Error::ParseErr),
}
}
}
pub fn build() -> TorrentBuilder {
TorrentBuilder::new()
}
pub fn start(&self) {
let mut tor = self.tr_torrent.write().unwrap();
unsafe {
transmission_sys::tr_torrentStart(tor.as_mut());
}
}
pub fn stop(&self) {
let mut tor = self.tr_torrent.write().unwrap();
unsafe {
transmission_sys::tr_torrentStop(tor.as_mut());
}
}
pub fn remove(self, with_data: bool) {
let mut tor = Arc::try_unwrap(self.tr_torrent)
.unwrap()
.into_inner()
.unwrap();
unsafe {
transmission_sys::tr_torrentRemove(tor.as_mut(), with_data, None);
}
}
pub fn verify(&self) {
let mut tor = self.tr_torrent.write().unwrap();
unsafe { transmission_sys::tr_torrentVerify(tor.as_mut(), None, null_mut()) }
}
pub fn name(&self) -> &str {
let tor = self.tr_torrent.read().unwrap();
unsafe {
let c_str = transmission_sys::tr_torrentName(tor.as_ref());
ffi::CStr::from_ptr(c_str).to_str().unwrap()
}
}
pub fn id(&self) -> i32 {
let tor = self.tr_torrent.read().unwrap();
unsafe { transmission_sys::tr_torrentId(tor.as_ref()) }
}
pub fn stats(&self) -> TorrentStats {
let mut tor = self.tr_torrent.write().unwrap();
unsafe { TorrentStats::from(transmission_sys::tr_torrentStatCached(tor.as_mut())) }
}
pub fn info(&self) -> TorrentInfo {
let tor = self.tr_torrent.read().unwrap();
let info;
unsafe {
info = transmission_sys::tr_torrentInfo(tor.as_ref());
}
TorrentInfo::from(unsafe { *info })
}
pub fn set_ratio(&mut self, limit: f64) {
let mut tor = self.tr_torrent.write().unwrap();
unsafe {
transmission_sys::tr_torrentSetRatioLimit(tor.as_mut(), limit);
}
}
pub fn set_download_dir(&mut self, download_dir: PathBuf) {
let mut tor = self.tr_torrent.write().unwrap();
let d_dir = ffi::CString::new(download_dir.to_str().unwrap()).unwrap();
unsafe {
transmission_sys::tr_torrentSetDownloadDir(tor.as_mut(), d_dir.as_ptr());
}
}
pub fn set_priority(&mut self, priority: Priority) {
let mut tor = self.tr_torrent.write().unwrap();
unsafe {
transmission_sys::tr_torrentSetPriority(tor.as_mut(), priority as i8);
}
}
pub fn get_file_index(&self, file: &TorrentFile) -> Option<usize> {
self.info()
.files
.iter()
.position(|e| e.name == file.name && e.length == file.length)
}
pub fn set_file_download(&mut self, file: TorrentFile, download: bool) {
self.set_files_download(vec![file], download);
}
pub fn set_files_download(&mut self, files: Vec<TorrentFile>, download: bool) {
let ids = files
.iter()
.filter_map(|f| self.get_file_index(f).and_then(|e| Some(e as u32)))
.collect();
self.set_files_download_by_id(ids, download);
}
pub fn set_files_download_by_id(&mut self, ids: Vec<u32>, download: bool) {
let mut tor = self.tr_torrent.write().unwrap();
unsafe {
transmission_sys::tr_torrentSetFileDLs(
tor.as_mut(),
ids.as_ptr(),
ids.len() as u32,
download,
);
}
}
pub fn set_file_priority(&mut self, file: TorrentFile, priority: Priority) {
self.set_files_priorities(vec![file], priority);
}
pub fn set_files_priorities(&mut self, files: Vec<TorrentFile>, priority: Priority) {
let ids = files
.iter()
.filter_map(|f| self.get_file_index(f).and_then(|e| Some(e as u32)))
.collect();
self.set_files_priorities_by_id(ids, priority);
}
pub fn set_files_priorities_by_id(&mut self, ids: Vec<u32>, priority: Priority) {
let mut tor = self.tr_torrent.write().unwrap();
unsafe {
transmission_sys::tr_torrentSetFilePriorities(
tor.as_mut(),
ids.as_ptr(),
ids.len() as u32,
priority as i8,
)
}
}
}
impl serde::ser::Serialize for Torrent {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("Torrent", 2)?;
state.serialize_field("info", &self.info())?;
state.serialize_field("stats", &self.stats())?;
state.end()
}
}
impl AsMut<Torrent> for Torrent {
fn as_mut(&mut self) -> &mut Self {
self
}
}
unsafe impl std::marker::Send for Torrent {}
unsafe impl std::marker::Sync for Torrent {}