use std::str::Chars;
use std::time::Duration;
use errors::CueError;
pub fn unescape_quotes(s: &str) -> String {
let mut unescaped = s.replace("\\\"", "\"");
if unescaped.ends_with('\"') && unescaped.starts_with('\"') {
unescaped.pop();
unescaped.remove(0);
}
unescaped
}
#[allow(dead_code)]
pub fn timestamp_to_duration(s: &str) -> Result<Duration, CueError> {
fn next_group(chars: &mut Chars) -> String {
chars.take_while(|c| *c != ':').collect::<String>()
}
let timestamp = s.to_string();
let mut iter = timestamp.chars();
let minutes: String = next_group(&mut iter);
let seconds: String = next_group(&mut iter);
let frames: String = iter.collect();
let frame_seconds = frames.parse::<f64>()? / 75.0;
let seconds =
minutes.parse::<u64>()? * 60 + seconds.parse::<u64>()? + frame_seconds.floor() as u64;
let nanos = (frame_seconds.fract() * 1_000_000_000f64) as u32;
Ok(Duration::new(seconds, nanos))
}
pub fn next_token(chars: &mut Chars) -> String {
chars.take_while(|c| !c.is_whitespace()).collect::<String>()
}
#[allow(dead_code)]
pub fn next_string(chars: &mut Chars, error: &str) -> Result<String, CueError> {
let first = chars
.next()
.ok_or_else(|| CueError::Parse(error.to_string()))?;
if first == '"' {
let mut escaped = false;
let string = chars
.take_while(|c| {
if !escaped && *c == '\\' {
escaped = true;
return true;
}
if escaped {
escaped = false;
return true;
}
*c != '"'
})
.collect::<String>();
let _next_space = chars.next().ok_or_else(|| {
CueError::Parse(
"Unexpected error: could not consume next space. This is likely a bug.".to_string(),
)
});
Ok(unescape_quotes(&string))
} else {
let string = first.to_string() + &next_token(chars);
Ok(unescape_quotes(&string))
}
}
#[allow(dead_code)]
pub fn next_values(chars: &mut Chars) -> Vec<String> {
let string: String = chars.collect();
string.split_whitespace().map(|s| s.to_string()).collect()
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[test]
fn test_unescape_quotes() {
let actual = unescape_quotes(r#""lmao \"i\"cons""#);
let expected = r#"lmao "i"cons"#;
assert_eq!(actual, expected);
}
#[test]
fn test_unescape_unescaped_string() {
let actual = unescape_quotes(r#"lmao "i"cons"#);
let expected = r#"lmao "i"cons"#;
assert_eq!(actual, expected);
}
#[test]
fn test_valid_timestamp_conversion() {
let actual = timestamp_to_duration("00:00:00").unwrap();
let expected = Duration::new(0, 0);
assert_eq!(actual, expected);
let actual = timestamp_to_duration("01:01:00").unwrap();
let expected = Duration::new(61, 0);
assert_eq!(actual, expected);
let actual = timestamp_to_duration("99:99:99").unwrap();
let expected = Duration::new(6040, 320000000);
assert_eq!(actual, expected);
}
#[test]
fn test_frame_second_conversion() {
let actual = timestamp_to_duration("00:00:75").unwrap();
let expected = Duration::new(1, 0);
assert_eq!(actual, expected);
let actual = timestamp_to_duration("00:00:76").unwrap();
let expected = Duration::new(1, 13333333);
assert_eq!(actual, expected);
}
#[test]
fn test_invalid_timestamp() {
assert!(timestamp_to_duration("000000").is_err());
assert!(timestamp_to_duration("-00:00:00").is_err());
assert!(timestamp_to_duration("00:00").is_err());
assert!(timestamp_to_duration("00 00 00").is_err());
assert!(timestamp_to_duration("00.00.00").is_err());
assert!(timestamp_to_duration(" 00:00:00").is_err());
assert!(timestamp_to_duration("00:00:00 ").is_err());
assert!(timestamp_to_duration(" 00:00:00 ").is_err());
assert!(timestamp_to_duration("P0003-06-04T12:30:05").is_err());
}
#[test]
fn test_next_string_quotation_marks() {
let quotes = r#""quotation \"\" marks""#.to_string();
let actual = next_string(&mut quotes.chars(), "").unwrap();
let expected = r#"quotation "" marks"#;
assert_eq!(actual, expected);
}
#[test]
fn test_next_string_single_quotation_marks() {
let quotes_single = r#"this\"isfine"#.to_string();
let actual = next_string(&mut quotes_single.chars(), "").unwrap();
let expected = r#"this"isfine"#;
assert_eq!(actual, expected);
}
#[test]
fn test_next_tokens() {
let tokens = "a b c d".to_string();
let mut iter = tokens.chars();
assert_eq!(next_token(&mut iter), "a".to_string());
assert_eq!(next_token(&mut iter), "b".to_string());
assert_eq!(next_token(&mut iter), "c".to_string());
assert_eq!(next_token(&mut iter), "d".to_string());
}
#[test]
fn test_next_values() {
let values = "a b".to_string();
let mut iter = values.chars();
let actual = next_values(&mut iter);
let expected = vec!["a".to_string(), "b".to_string()];
assert_eq!(actual, expected);
}
}