qbt-clean 0.155.0

Automated rules-based cleaning of qBittorrent torrents.
#[derive(Default)]
pub struct QbtMock {
	pub deleted: std::sync::Mutex<Vec<crate::InfoHash>>,
	pub free_space: u64,
	pub torrents: Vec<crate::MockTorrent>,
	pub trackers: Vec<crate::Tracker>,
}

impl QbtMock {
	pub fn with_trackers(
		trackers: impl IntoIterator<Item = crate::Tracker>,
	) -> Self {
		Self {
			trackers: trackers.into_iter().collect(),
			..Default::default()
		}
	}

	pub fn deleted(&self) -> Vec<crate::InfoHash> {
		self.deleted.lock().unwrap().clone()
	}
}

pub fn global_with_trackers(
	config: crate::Config,
	now: std::time::SystemTime,
	trackers: impl IntoIterator<Item = crate::Tracker>,
) -> crate::Global<QbtMock> {
	crate::Global {
		config,
		now,
		qbt: QbtMock::with_trackers(trackers),
	}
}

impl crate::Qbt for QbtMock {
	async fn logout(self) -> anyhow::Result<()> {
		unimplemented!("QbtMock::logout")
	}

	async fn server_state(&self) -> anyhow::Result<crate::ServerState> {
		Ok(crate::ServerState {
			free_space_on_disk: self.free_space,
		})
	}

	async fn torrents_info(&self) -> anyhow::Result<Vec<crate::Torrent>> {
		Ok(self.torrents.iter()
			.map(|t| t.torrent.clone())
			.collect())
	}

	async fn torrent_files(&self, hash: crate::InfoHash)
		-> anyhow::Result<Vec<crate::TorrentFile>>
	{
		let t = self.torrents.iter()
			.find(|t| t.torrent.hash == hash)
			.ok_or_else(|| anyhow::format_err!("no torrent for {hash}"))?;
		Ok(t.files.clone())
	}

	async fn torrent_trackers(&self, _: crate::InfoHash)
		-> anyhow::Result<Vec<crate::Tracker>>
	{
		Ok(self.trackers.clone())
	}

	async fn delete_torrents(&self, hashes: &[crate::InfoHash]) -> anyhow::Result<()> {
		self.deleted.lock().unwrap().extend_from_slice(hashes);
		Ok(())
	}
}