1#![forbid(unsafe_code)]
2#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct Bitrate {
21 bits_per_second: u64,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum BitrateError {
26 InvalidBitrate,
27 InvalidDuration,
28}
29
30fn validate_bitrate(bits_per_second: u64) -> Result<u64, BitrateError> {
31 if bits_per_second == 0 {
32 Err(BitrateError::InvalidBitrate)
33 } else {
34 Ok(bits_per_second)
35 }
36}
37
38fn validate_duration(duration_seconds: f64) -> Result<f64, BitrateError> {
39 if !duration_seconds.is_finite() || duration_seconds <= 0.0 {
40 Err(BitrateError::InvalidDuration)
41 } else {
42 Ok(duration_seconds)
43 }
44}
45
46impl Bitrate {
47 pub fn new(bits_per_second: u64) -> Result<Self, BitrateError> {
48 Ok(Self {
49 bits_per_second: validate_bitrate(bits_per_second)?,
50 })
51 }
52
53 #[must_use]
54 pub fn bits_per_second(&self) -> u64 {
55 self.bits_per_second
56 }
57
58 #[must_use]
59 pub fn kilobits_per_second(&self) -> f64 {
60 kilobits_per_second(self.bits_per_second)
61 }
62
63 #[must_use]
64 pub fn megabits_per_second(&self) -> f64 {
65 megabits_per_second(self.bits_per_second)
66 }
67}
68
69pub fn bits_for_duration(bits_per_second: u64, duration_seconds: f64) -> Result<u64, BitrateError> {
70 let bits_per_second = validate_bitrate(bits_per_second)?;
71 let duration_seconds = validate_duration(duration_seconds)?;
72 Ok((bits_per_second as f64 * duration_seconds).round() as u64)
73}
74
75pub fn bytes_for_duration(
76 bits_per_second: u64,
77 duration_seconds: f64,
78) -> Result<u64, BitrateError> {
79 let bits_per_second = validate_bitrate(bits_per_second)?;
80 let duration_seconds = validate_duration(duration_seconds)?;
81 Ok(((bits_per_second as f64 * duration_seconds) / 8.0).round() as u64)
82}
83
84pub fn bitrate_from_size(size_bytes: u64, duration_seconds: f64) -> Result<u64, BitrateError> {
85 let duration_seconds = validate_duration(duration_seconds)?;
86 Ok(((size_bytes as f64 * 8.0) / duration_seconds).round() as u64)
87}
88
89#[must_use]
90pub fn kilobits_per_second(bits_per_second: u64) -> f64 {
91 bits_per_second as f64 / 1_000.0
92}
93
94#[must_use]
95pub fn megabits_per_second(bits_per_second: u64) -> f64 {
96 bits_per_second as f64 / 1_000_000.0
97}
98
99#[cfg(test)]
100mod tests {
101 use super::{
102 Bitrate, BitrateError, bitrate_from_size, bits_for_duration, bytes_for_duration,
103 kilobits_per_second, megabits_per_second,
104 };
105
106 #[test]
107 fn computes_bitrate_size_helpers() {
108 let bitrate = Bitrate::new(8_000_000).unwrap();
109
110 assert_eq!(bitrate.bits_per_second(), 8_000_000);
111 assert_eq!(bits_for_duration(8_000_000, 10.0).unwrap(), 80_000_000);
112 assert_eq!(bytes_for_duration(8_000_000, 10.0).unwrap(), 10_000_000);
113 assert_eq!(bitrate_from_size(10_000_000, 10.0).unwrap(), 8_000_000);
114 assert_eq!(kilobits_per_second(8_000_000), 8_000.0);
115 assert_eq!(megabits_per_second(8_000_000), 8.0);
116 assert_eq!(bitrate.kilobits_per_second(), 8_000.0);
117 assert_eq!(bitrate.megabits_per_second(), 8.0);
118 }
119
120 #[test]
121 fn rejects_invalid_bitrate_inputs() {
122 assert_eq!(Bitrate::new(0), Err(BitrateError::InvalidBitrate));
123 assert_eq!(
124 bits_for_duration(0, 10.0),
125 Err(BitrateError::InvalidBitrate)
126 );
127 assert_eq!(
128 bytes_for_duration(8_000_000, 0.0),
129 Err(BitrateError::InvalidDuration)
130 );
131 assert_eq!(
132 bitrate_from_size(10_000_000, f64::NAN),
133 Err(BitrateError::InvalidDuration)
134 );
135 }
136}