use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
use std::ffi::{CStr, c_char};
use crate::{errors::GalionError, librclone::bindings as librclone_bindings};
#[derive(Debug, Default)]
pub struct Rclone {
librclone_is_initialized: bool,
}
impl Drop for Rclone {
fn drop(&mut self) {
self.finalize();
}
}
impl Rclone {
#[must_use]
pub fn new() -> Self {
let mut rclone = Self::default();
rclone.initialize();
rclone
}
pub fn initialize(&mut self) {
if !self.librclone_is_initialized {
unsafe { librclone_bindings::RcloneInitialize() };
self.librclone_is_initialized = true;
}
}
pub fn finalize(&mut self) {
if self.librclone_is_initialized {
unsafe { librclone_bindings::RcloneFinalize() }
self.librclone_is_initialized = false;
}
}
pub fn rpc(&self, method: &str, input: &Value) -> Result<String, String> {
let method_bytes = method.as_bytes();
let mut method_c_chars: Vec<c_char> = method_bytes
.iter()
.map(|c| (*c).cast_signed())
.collect::<Vec<c_char>>();
method_c_chars.push(0); let method_mut_ptr: *mut c_char = method_c_chars.as_mut_ptr();
let input_bytes: Vec<u8> = input.to_string().into_bytes();
let mut input_c_chars: Vec<c_char> = input_bytes
.iter()
.map(|c| (*c).cast_signed())
.collect::<Vec<c_char>>();
input_c_chars.push(0); let input_mut_ptr: *mut c_char = input_c_chars.as_mut_ptr();
let result = unsafe { librclone_bindings::RcloneRPC(method_mut_ptr, input_mut_ptr) };
let output_c_str: &CStr = unsafe { CStr::from_ptr(result.Output) };
let output_slice: &str = output_c_str
.to_str()
.map_err(|e| format!("Error decoding the rclone RPC output: {e}"))?;
let output: String = output_slice.to_owned();
unsafe { librclone_bindings::RcloneFreeString(result.Output) };
match result.Status {
200 => Ok(output),
_ => Err(output),
}
}
pub fn rc_noop(&self, value: &Value) -> Result<Value, GalionError> {
let res = self.rpc("rc/noop", value)?;
let value = serde_json::from_str::<Value>(&res)?;
Ok(value)
}
pub fn rc_gc(&self) -> Result<(), GalionError> {
self.rpc("core/gc", &json!({}))?;
Ok(())
}
pub fn get_rpc_config(&self) -> Result<Value, GalionError> {
let res = self.rpc("options/get", &json!({}))?;
let value = serde_json::from_str::<Value>(&res)?;
Ok(value)
}
pub fn set_config_options(&self, conf: &Value) -> Result<Value, GalionError> {
let res = self.rpc("options/set", conf)?;
let value = serde_json::from_str::<Value>(&res)?;
Ok(value)
}
pub fn set_config_path(&self, config_path: &str) -> Result<Value, GalionError> {
let input_json = json!({
"path": config_path
});
let res = self.rpc("config/setpath", &input_json)?;
let value = serde_json::from_str::<Value>(&res)?;
Ok(value)
}
pub fn dump_config(&self) -> Result<Value, GalionError> {
let res = self.rpc("config/dump", &json!({}))?;
let value = serde_json::from_str::<Value>(&res)?;
Ok(value)
}
pub fn list_remotes(&self) -> Result<Vec<String>, GalionError> {
let res = self.rpc("config/listremotes", &json!({}))?;
let value = serde_json::from_str::<Value>(&res)?;
match value {
Value::Object(arr) => match arr.get("remotes") {
Some(Value::Array(remotes_list)) => {
let mut remotes = Vec::new();
for remote in remotes_list {
if let Value::String(remote_name) = remote {
remotes.push(remote_name.clone());
}
}
Ok(remotes)
}
_ => Ok(vec![]),
},
_ => Err("Bad response - no remotes".into()),
}
}
pub fn get_remote(&self, remote_name: &str) -> Result<Value, GalionError> {
let res = self.rpc("config/get", &json!({"name": remote_name}))?;
let value = serde_json::from_str::<Value>(&res)?;
Ok(value)
}
pub fn sync<Src: AsRef<str>, Dest: AsRef<str>>(
&self,
src_fs: Src,
dest_fs: Dest,
is_async: bool,
) -> Result<Value, GalionError> {
match self.rpc(
"sync/sync",
&json!({
"srcFs": src_fs.as_ref(),
"dstFs": dest_fs.as_ref(),
"_async": is_async,
}),
) {
Ok(res) => {
let value = serde_json::from_str::<Value>(&res)?;
Ok(value)
}
Err(e) => {
let value = serde_json::from_str::<Value>(&e)?;
Err(value.into())
}
}
}
pub fn job_list(&self) -> Result<RcJobList, GalionError> {
let res = self.rpc("job/list", &json!({}))?;
let list = serde_json::from_str::<RcJobList>(&res)?;
Ok(list)
}
pub fn job_status(&self, job_id: u64) -> Result<Value, GalionError> {
let res = self.rpc("job/status", &json!({ "jobid": job_id }))?;
let value = serde_json::from_str::<Value>(&res)?;
Ok(value)
}
}
#[derive(Debug, Deserialize, Serialize)]
pub struct RcJobList {
#[serde(rename = "jobids")]
pub job_ids: Vec<u64>,
#[serde(rename = "runningIds")]
pub running_ids: Vec<u64>,
#[serde(rename = "finishedIds")]
pub finished_ids: Vec<u64>,
}