qbt-clean 0.128.0

Automated rules-based cleaning of qBittorrent torrents.
#[serde_with::serde_as]
#[derive(Debug,serde::Deserialize)]
pub struct Torrent {
	#[serde(rename="num_complete")] pub seeders: u64,
	#[serde(rename="num_incomplete")] pub leechers: u64,
	#[serde(rename="size")] pub selected_size: u64,
	#[serde_as(as="serde_with::DurationSeconds")] pub seeding_time: std::time::Duration,
	#[serde_as(as="serde_with::TimestampSeconds")] pub added_on: std::time::SystemTime,
	#[serde_as(as="serde_with::TimestampSeconds")] pub last_activity: std::time::SystemTime,
	pub category: String,
	pub hash: crate::InfoHash,
	pub name: String,
	pub private: Option<bool>, // Can be null when downloading metadata.
	pub ratio: f32,
	pub state: crate::TorrentState,
	pub total_size: i64, // Can be -1 if the size is unknown.
	pub tracker: String,
}

impl Torrent {
	#[cfg(test)]
	pub fn zero() -> Self {
		Self {
			seeders: 0,
			leechers: 0,
			selected_size: 0,
			seeding_time: std::time::Duration::ZERO,
			added_on: std::time::SystemTime::UNIX_EPOCH,
			last_activity: std::time::SystemTime::UNIX_EPOCH,
			category: String::new(),
			hash: crate::InfoHash::example(),
			name: String::new(),
			private: None,
			ratio: 0.0,
			state: crate::TorrentState::Unknown,
			total_size: 0,
			tracker: String::new(),
		}
	}

	pub fn cmp_natural_key(&self) -> impl Ord {
		(&self.tracker, &self.hash)
	}

	pub fn tracker(&self) -> Result<url::Url, url::ParseError> {
		self.tracker.parse()
	}

	pub fn tracker_host(&self) -> Option<String> {
		Some(
			self.tracker().ok()?
				.host_str()?
				.into())
	}
}