use std::ffi;
use std::fs::canonicalize;
use std::ptr::NonNull;
use std::sync::{Arc, RwLock};
use transmission_sys;
use super::ClientConfig;
use crate::error::{Error, TrResult};
use crate::torrent::Torrent;
#[derive(Clone)]
pub struct Client {
tr_session: Arc<RwLock<NonNull<transmission_sys::tr_session>>>,
}
impl Client {
pub fn new(config: ClientConfig) -> Self {
let c_dir = config
.config_dir
.clone()
.expect("Configuration directory not set!");
let c_dir = ffi::CString::new(c_dir.to_str().unwrap()).unwrap();
let app_name = config.app_name.clone().expect("Application name not set!");
let app_name = ffi::CString::new(app_name).unwrap();
let ses;
unsafe {
let mut set = config.to_variant();
transmission_sys::tr_sessionLoadSettings(&mut set, c_dir.as_ptr(), app_name.as_ptr());
ses = transmission_sys::tr_sessionInit(c_dir.as_ptr(), false, &mut set);
transmission_sys::tr_variantFree(&mut set);
}
Self {
tr_session: Arc::new(RwLock::new(NonNull::new(ses).unwrap())),
}
}
pub fn add_torrent_file(&self, path: &str) -> TrResult<Torrent> {
let path = canonicalize(path).unwrap();
let path = ffi::CString::new(path.to_str().unwrap()).unwrap();
let mut ses = self.tr_session.write().unwrap();
let ctor;
unsafe {
ctor = transmission_sys::tr_ctorNew(ses.as_mut());
}
match unsafe { transmission_sys::tr_ctorSetMetainfoFromFile(ctor, path.as_ptr()) } {
0 => Torrent::from_ctor(ctor),
_ => Err(Error::Unknown),
}
}
pub fn add_torrent_magnet(&self, link: &str) -> TrResult<Torrent> {
let link = ffi::CString::new(link).unwrap();
let mut ses = self.tr_session.write().unwrap();
let ctor;
unsafe {
ctor = transmission_sys::tr_ctorNew(ses.as_mut());
}
match unsafe { transmission_sys::tr_ctorSetMetainfoFromMagnetLink(ctor, link.as_ptr()) } {
0 => Torrent::from_ctor(ctor),
_ => Err(Error::Unknown),
}
}
pub fn close(self) {}
}
impl Drop for Client {
fn drop(&mut self) {
if Arc::strong_count(&self.tr_session) == 1 {
let ses = self.tr_session.write().unwrap();
unsafe {
transmission_sys::tr_sessionClose(ses.as_ptr());
}
}
}
}
unsafe impl std::marker::Send for Client {}
unsafe impl std::marker::Sync for Client {}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
#[test]
fn thread_safe() {
let test_dir = "/tmp/tr-test-thread";
std::fs::create_dir(test_dir).unwrap();
let c = ClientConfig::new()
.app_name("testing")
.config_dir(test_dir)
.download_dir(test_dir);
let client = Client::new(c);
thread::spawn(move || client.close());
std::fs::remove_dir_all(test_dir).unwrap_or(());
}
}