1use crate::{timecode_parse::round_seconds_to_frame, Framerate, TimecodeParseError};
2use core::result::Result;
3use core::result::Result::Ok;
4use num::Rational32;
5use num::{FromPrimitive, Rational64};
6use regex::Match;
7
8use crate::consts::{RUNTIME_REGEX, SECONDS_PER_HOUR_I64, SECONDS_PER_MINUTE_I64};
9use crate::timecode_parse::convert_tc_int;
10use std::fmt::Debug;
11
12pub type SecondsSourceResult = Result<num::Rational64, TimecodeParseError>;
14
15pub trait SecondsSource: Debug {
18 fn to_seconds(&self, rate: Framerate) -> SecondsSourceResult;
20}
21
22impl SecondsSource for &dyn SecondsSource {
23 fn to_seconds(&self, rate: Framerate) -> SecondsSourceResult {
24 (*self).to_seconds(rate)
25 }
26}
27
28impl<T> SecondsSource for &T
29where
30 T: SecondsSource,
31{
32 fn to_seconds(&self, rate: Framerate) -> SecondsSourceResult {
33 (*self).to_seconds(rate)
34 }
35}
36
37impl SecondsSource for num::Rational64 {
40 fn to_seconds(&self, _: Framerate) -> SecondsSourceResult {
41 Ok(*self)
42 }
43}
44
45impl SecondsSource for f64 {
46 fn to_seconds(&self, _: Framerate) -> SecondsSourceResult {
47 let rat32 = match Rational32::from_f64(*self) {
56 None => {
57 return Err(TimecodeParseError::Conversion(
58 "could not convert f64 to Rational64".to_string(),
59 ))
60 }
61 Some(parsed) => parsed,
62 };
63
64 Ok(Rational64::new(
65 *rat32.numer() as i64,
66 *rat32.denom() as i64,
67 ))
68 }
69}
70
71impl SecondsSource for f32 {
72 fn to_seconds(&self, rate: Framerate) -> SecondsSourceResult {
73 f64::from(*self).to_seconds(rate)
75 }
76}
77
78impl SecondsSource for &str {
79 fn to_seconds(&self, rate: Framerate) -> SecondsSourceResult {
80 if let Some(matched) = RUNTIME_REGEX.captures(self) {
81 return parse_runtime_str(matched, rate);
82 }
83
84 Err(TimecodeParseError::UnknownStrFormat(format!(
85 "{} is not a known seconds timecode format",
86 self
87 )))
88 }
89}
90
91impl SecondsSource for String {
92 fn to_seconds(&self, rate: Framerate) -> SecondsSourceResult {
93 self.as_str().to_seconds(rate)
94 }
95}
96
97fn parse_runtime_str(matched: regex::Captures, rate: Framerate) -> SecondsSourceResult {
98 let mut sections: Vec<Match> = Vec::new();
104 if let Some(section) = matched.name("section1") {
105 sections.push(section);
106 };
107 if let Some(section) = matched.name("section2") {
108 sections.push(section);
109 };
110
111 let is_negative = matched.name("negative").is_some();
113
114 let minutes: i64 = match sections.pop() {
115 None => 0,
116 Some(section) => convert_tc_int(section.as_str(), "minutes")?,
117 };
118
119 let hours: i64 = match sections.pop() {
120 None => 0,
121 Some(section) => convert_tc_int(section.as_str(), "frames")?,
122 };
123
124 let seconds_str = matched.name("seconds").unwrap().as_str();
126 let seconds_split = seconds_str.split('.').collect::<Vec<&str>>();
127
128 let mut seconds = convert_tc_int(seconds_split[0], "seconds")?;
130 seconds += hours * SECONDS_PER_HOUR_I64 + minutes * SECONDS_PER_MINUTE_I64;
131
132 let maybe_fractal = seconds_split.get(1);
136 let seconds_fractal_str = if let Some(seconds_fractal_str) = maybe_fractal {
137 let mut fixed_fractal = "0.".to_string();
138 fixed_fractal.push_str(seconds_fractal_str);
139 fixed_fractal
140 } else {
141 "0.0".to_string()
142 };
143
144 let seconds_fractal = match seconds_fractal_str.parse::<f64>() {
146 Ok(parsed) => parsed,
147 Err(err) => {
148 return Err(TimecodeParseError::Conversion(format!(
149 "error conversion seconds of runtime to f64: {}",
150 err
151 )))
152 }
153 };
154
155 let seconds_fractal_rat32 = match Rational32::from_f64(seconds_fractal) {
160 None => {
161 return Err(TimecodeParseError::Conversion(
162 "error conversion fractal seconds of runtime to rational".to_string(),
163 ))
164 }
165 Some(parsed) => parsed,
166 };
167
168 let mut seconds_fractal_rat64 = Rational64::new(
169 *seconds_fractal_rat32.numer() as i64,
170 *seconds_fractal_rat32.denom() as i64,
171 );
172
173 seconds_fractal_rat64 = round_seconds_to_frame(seconds_fractal_rat64, rate);
177
178 let mut seconds_rat = Rational64::from_integer(seconds) + seconds_fractal_rat64;
181 if is_negative {
182 seconds_rat = -seconds_rat
183 }
184
185 seconds_rat.to_seconds(rate)
187}