substudy 0.5.2

Language-learning tools for working with parallel, bilingual subtitles and media files.
Documentation
//! Tools for studying foreign languages using subtitles.  All APIs are
//! currently experimental or unstable, but if you'd like me to stabilize
//! things, please get in touch.
//!
//! For further details about how to use substudy, see [the main GitHub
//! page](http://github.com/emk/substudy).

#![warn(missing_docs)]

pub use anyhow::{Error, Result};

pub mod align;
pub mod clean;
pub mod contexts;
pub mod decode;
pub mod errors;
pub mod export;
pub mod import;
pub mod lang;
pub mod merge;
pub(crate) mod progress;
pub mod segment;
pub mod services;
pub mod srt;
pub mod time;
pub mod video;

peg::parser! {
    grammar grammar() for str {
        use std::str::FromStr;

        use srt::{Subtitle, SubtitleFile};
        use time::Period;

        pub rule subtitle_file() -> SubtitleFile
            = blank_lines()? result:subtitles() blank_lines()? {
                SubtitleFile { subtitles: result }
            }

        rule subtitles() -> Vec<Subtitle>
            = subs:subtitle() ** blank_lines() { subs }

        rule subtitle() -> Subtitle
            = index:digits() newline() p:time_period() newline() l:lines() {
                Subtitle { index: index, period: p, lines: l }
            }

        rule time_period() -> Period
            = begin:time() " --> " end:time() {?
                let mut end = end;
                if begin == end {
                    // If subtitle has zero length, fix it. These are generated by
                    // the Aeneas audio/text alignment tool, which is otherwise
                    // excellent for use with audiobooks.
                    end += 0.001;
                }
                match Period::new(begin, end) {
                  Ok(p) => Ok(p),
                  Err(_) => Err("invalid time period"),
                }
            }

        rule time() -> f32
            = hh:digits() ":" mm:digits() ":" ss:comma_float() {
                (hh as f32)*3600.0 + (mm as f32)*60.0 + ss
            }

        rule lines() -> Vec<String>
            = lines:line() ** newline() { lines }

        rule line() -> String
            = text:$([^ '\r' | '\n']+) { text.to_string() }

        rule digits() -> usize
            = digits:$(['0'..='9']+) { FromStr::from_str(digits).unwrap() }

        rule comma_float() -> f32
            = float_str:$(['0'..='9']+ "," ['0'..='9']+) {
                let fixed: String = float_str.replace(",", ".");
                FromStr::from_str(&fixed).unwrap()
            }

        rule newline()
            = "\r"? "\n"

        rule blank_lines()
            = newline()+
    }
}