1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#[macro_use]
extern crate lazy_static;
extern crate itertools;
extern crate regex;

use itertools::Itertools;
use regex::Regex;

struct Episode<'a> {
    fullname: &'a str,
    name: &'a str,
    episode: i32,
    season: i32,
}

pub fn possible_next_episode<'a>(current_episode: &str, episodes: &'a [&str]) -> Option<&'a str> {
    parse_episode(current_episode).and_then(|curr_ep| {
        episodes
            .iter()
            .map(|m| parse_episode(m))
            .filter_map(|f| f)
            .filter(|f| f.name == curr_ep.name)
            .filter(|f| {
                (f.episode > curr_ep.episode && f.season == curr_ep.season)
                    || f.season > curr_ep.season
            })
            .sorted_by_key(|k| (k.season, k.episode))
            .first()
            .map(|m| m.fullname)
    })
}

fn parse_episode(episode: &str) -> Option<Episode> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"((?i)^(.+).s(\d+)e(\d+).*)$").unwrap();
    }

    RE.captures(episode).and_then(|caps| {
        match (
            caps.get(1).unwrap().as_str(),
            caps.get(2).unwrap().as_str(),
            caps.get(4).unwrap().as_str().parse::<i32>(),
            caps.get(3).unwrap().as_str().parse::<i32>(),
        ) {
            (fullname, name, Ok(episode), Ok(season)) => Some(Episode {
                fullname,
                name,
                episode,
                season,
            }),
            _ => None,
        }
    })
}

#[cfg(test)]
mod tests {
    use super::*;

    const EP_LIST: &'static [&'static str] = &[
        "SomeSeries.S01E01.1080p.SomeFormat.mkv",
        "SomeSeries.S01E05.720p.SomeFormat2.mkv",
        "SomeSeries.S01E02.720p.Format3.mkv",
        "Fav.Series.S01E01.720p.Format1.mkv",
        "Fav.Series.S02E02.1080p.Format2.mkv",
        "Fav.Series.S03E02.720p.Format3.mkv",
    ];

    #[test]
    fn next_ep() {
        let nxt = possible_next_episode("SomeSeries.S01E01.SomeFormat.mkv", &EP_LIST);
        assert_eq!(nxt, Some("SomeSeries.S01E02.720p.Format3.mkv"));
    }

    #[test]
    fn next_ep_sprase() {
        let nxt = possible_next_episode("SomeSeries.S01E02.720p.Format3.mkv", &EP_LIST);
        assert_eq!(nxt, Some("SomeSeries.S01E05.720p.SomeFormat2.mkv"));
    }

    #[test]
    fn across_season() {
        let nxt = possible_next_episode("Fav.Series.S01E01.720p.Format1.mkv", &EP_LIST);
        assert_eq!(nxt, Some("Fav.Series.S02E02.1080p.Format2.mkv"));
    }

    #[test]
    fn last_ep() {
        let nxt = possible_next_episode("Fav.Series.S03E02.720p.Format3.mkv", &EP_LIST);
        assert_eq!(nxt, None);
    }
}