diff options
author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2024-08-21 10:49:23 +0200 |
---|---|---|
committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2024-08-21 11:28:43 +0200 |
commit | 1debeb77f7986de1b659dcfdc442de6415e1d9f5 (patch) | |
tree | 4df3e7c3f6a2d1ec116e4088c5ace7f143a8b05f /src/select/selection_file/duration.rs | |
download | yt-1debeb77f7986de1b659dcfdc442de6415e1d9f5.tar.gz yt-1debeb77f7986de1b659dcfdc442de6415e1d9f5.zip |
chore: Initial Commit
This repository was migrated out of my nixos-config.
Diffstat (limited to '')
-rw-r--r-- | src/select/selection_file/duration.rs | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/src/select/selection_file/duration.rs b/src/select/selection_file/duration.rs new file mode 100644 index 0000000..4224ead --- /dev/null +++ b/src/select/selection_file/duration.rs @@ -0,0 +1,102 @@ +// yt - A fully featured command line YouTube client +// +// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de> +// SPDX-License-Identifier: GPL-3.0-or-later +// +// This file is part of Yt. +// +// You should have received a copy of the License along with this program. +// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>. + +use std::str::FromStr; + +use anyhow::{Context, Result}; + +#[derive(Copy, Clone, Debug)] +pub struct Duration { + time: u32, +} + +impl FromStr for Duration { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + fn parse_num(str: &str, suffix: char) -> Result<u32> { + str.strip_suffix(suffix) + .expect("it has a 'h' suffix") + .parse::<u32>() + .context("Failed to parse hours") + } + + let buf: Vec<_> = s.split(' ').collect(); + + let hours; + let minutes; + let seconds; + + assert_eq!(buf.len(), 2, "Other lengths should not happen"); + + if buf[0].ends_with('h') { + hours = parse_num(buf[0], 'h')?; + minutes = parse_num(buf[1], 'm')?; + seconds = 0; + } else if buf[0].ends_with('m') { + hours = 0; + minutes = parse_num(buf[0], 'm')?; + seconds = parse_num(buf[1], 's')?; + } else { + unreachable!("The first part always ends with 'h' or 'm'") + } + + Ok(Self { + time: (hours * 60 * 60) + (minutes * 60) + seconds, + }) + } +} + +impl From<Option<f64>> for Duration { + fn from(value: Option<f64>) -> Self { + Self { + time: value.unwrap_or(0.0).ceil() as u32, + } + } +} + +impl std::fmt::Display for Duration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + const SECOND: u32 = 1; + const MINUTE: u32 = 60 * SECOND; + const HOUR: u32 = 60 * MINUTE; + + let base_hour = self.time - (self.time % HOUR); + let base_min = (self.time % HOUR) - ((self.time % HOUR) % MINUTE); + let base_sec = (self.time % HOUR) % MINUTE; + + let h = base_hour / HOUR; + let m = base_min / MINUTE; + let s = base_sec / SECOND; + + if self.time == 0 { + write!(f, "[No Duration]") + } else if h > 0 { + write!(f, "{h}h {m}m") + } else { + write!(f, "{m}m {s}s") + } + } +} +#[cfg(test)] +mod test { + use super::Duration; + + #[test] + fn test_display_duration_1h() { + let dur = Duration { time: 60 * 60 }; + assert_eq!("1h 0m".to_owned(), dur.to_string()); + } + #[test] + fn test_display_duration_30min() { + let dur = Duration { time: 60 * 30 }; + assert_eq!("30m 0s".to_owned(), dur.to_string()); + } +} |