qbt-clean 0.128.0

Automated rules-based cleaning of qBittorrent torrents.
pub struct FmtBytes(pub u64);

impl std::fmt::Display for FmtBytes {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		let mut prefix = 0;
		let mut whole = self.0;
		let mut fracional = 0;
		while whole >= 1000 && prefix < crate::SI_PREFIXES.len() {
			fracional = whole % 1024;
			whole /= 1024;
			prefix += 1;
		}
		if prefix == 0 {
			write!(f, "{} B", self.0)
		} else {
			write!(f,
				"{}.{:02}{}iB",
				whole,
				fracional * 100 / 1024,
				&crate::SI_PREFIXES[(prefix - 1)..][..1])
		}
	}
}

#[derive(Debug)]
pub struct FmtDuration(pub std::time::Duration);

impl std::fmt::Display for FmtDuration {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		let mut n = self.0
			.max(std::time::Duration::from_nanos(1)) // Don't tell them to wait for zero time.
			.as_secs();
		if self.0.subsec_nanos() > 0 {
			n += 1; // Round up.
		}

		let v = [
			(60, "s"),
			(60, "min"),
			(24, "h"),
			(0, "d"),
		];

		for ((small_div, small_u), &(big_div, big_u)) in v.into_iter().zip(&v[1..]) {
			let small_v = n % small_div;
			n /= small_div;

			if big_div != 0 && n >= big_div {
				if small_v > 0 {
					n += 1; // Round up.
				}
				continue
			}

			return if n == 0 {
				debug_assert_eq!(small_u, "s", "0 should be seconds");
				write!(f, "{}{}", small_v, small_u)
			} else if small_v == 0 {
				write!(f, "{}{}", n, big_u)
			} else {
				write!(f, "{}{} {}{}", n, big_u, small_v, small_u)
			}
		}

		unreachable!();
	}
}

pub struct FmtEither<A, B>(pub Result<A, B>);

impl<
	A: std::fmt::Display,
	B: std::fmt::Display,
> std::fmt::Display for FmtEither<A, B> {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		match &self.0 {
			Ok(a) => a.fmt(f),
			Err(b) => b.fmt(f),
		}
	}
}