use crate::services::putio;
use anyhow::Result;
use log::{debug, error, info, warn};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
const CONFIG_KEY: &str = "putioarr_transfers";
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransferState {
pub hash: String,
pub source_category: String,
pub download_dir: String,
}
#[derive(Clone)]
pub struct StateManager {
api_token: String,
transfers: Arc<RwLock<HashMap<String, TransferState>>>,
}
impl StateManager {
pub fn new(api_token: String) -> Self {
Self {
api_token,
transfers: Arc::new(RwLock::new(HashMap::new())),
}
}
pub async fn load(&self) -> Result<()> {
match putio::get_config_value::<HashMap<String, TransferState>>(&self.api_token, CONFIG_KEY)
.await
{
Ok(Some(map)) => {
let count = map.len();
*self.transfers.write().await = map;
info!("state: loaded {} transfer(s) from put.io config", count);
}
Ok(None) => debug!("state: no persisted state found in put.io config"),
Err(e) => warn!("state: failed to load persisted state from put.io: {}", e),
}
Ok(())
}
async fn persist(&self) {
let map = self.transfers.read().await.clone();
if let Err(e) = putio::set_config_value(&self.api_token, CONFIG_KEY, &map).await {
error!("state: failed to persist state to put.io: {}", e);
}
}
pub async fn add_transfer(
&self,
hash: String,
category: String,
download_dir: String,
) -> Result<()> {
let key = hash.to_lowercase();
debug!(
"state: add_transfer hash={} category={} dir={}",
key, category, download_dir
);
{
let mut transfers = self.transfers.write().await;
transfers.insert(
key.clone(),
TransferState {
hash: key,
source_category: category,
download_dir,
},
);
}
self.persist().await;
Ok(())
}
pub async fn get_transfer(&self, hash: &str) -> Option<TransferState> {
let transfers = self.transfers.read().await;
transfers.get(&hash.to_lowercase()).cloned()
}
pub async fn remove_transfer(&self, hash: &str) -> Result<()> {
{
let mut transfers = self.transfers.write().await;
transfers.remove(&hash.to_lowercase());
}
self.persist().await;
Ok(())
}
pub async fn get_download_dir_for_transfer(&self, hash: &str, default_dir: &str) -> String {
if let Some(state) = self.get_transfer(hash).await {
state.download_dir
} else {
debug!(
"state: no entry for hash={} (using default dir {})",
hash, default_dir
);
default_dir.to_string()
}
}
}