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
93
94
95
96
97
98
use crate::{MediaType, SerieID};
use eyre::ensure;
use std::str::FromStr;

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Target {
    /// Serie ID.
    pub id: SerieID,
    /// Media type to download.
    pub kind: MediaType,
    /// Episode or volume number.
    pub number: Vec<u16>,
}

impl FromStr for Target {
    type Err = eyre::Report;

    fn from_str(value: &str) -> Result<Self, Self::Err> {
        let parts = value.split(':').collect::<Vec<_>>();
        ensure!(
            !parts.is_empty() && parts.len() <= 3,
            "invalid target string"
        );
        let id = parts[0].parse()?;
        let kind = if parts.len() > 1 {
            parts[1].parse()?
        } else {
            MediaType::Episode
        };
        let number = if parts.len() > 2 {
            parts[2]
                .split(',')
                .map(str::parse)
                .collect::<Result<_, _>>()?
        } else {
            Vec::new()
        };

        Ok(Self { id, kind, number })
    }
}

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

    #[test]
    fn parse_full_episode() {
        let s = "32:episode:1,2,3,4,10";
        let expected = Target {
            id: 32.into(),
            kind: MediaType::Episode,
            number: vec![1, 2, 3, 4, 10],
        };
        let result: Target = s.parse().expect("valid input");

        assert_eq!(result, expected);
    }

    #[test]
    fn parse_full_volume() {
        let s = "44:volume:1";
        let expected = Target {
            id: 44.into(),
            kind: MediaType::Volume,
            number: vec![1],
        };
        let result: Target = s.parse().expect("valid input");

        assert_eq!(result, expected);
    }

    #[test]
    fn parse_no_number() {
        let s = "51:volume";
        let expected = Target {
            id: 51.into(),
            kind: MediaType::Volume,
            number: vec![],
        };
        let result: Target = s.parse().expect("valid input");

        assert_eq!(result, expected);
    }

    #[test]
    fn parse_only_id() {
        let s = "42";
        let expected = Target {
            id: 42.into(),
            kind: MediaType::Episode,
            number: vec![],
        };
        let result: Target = s.parse().expect("valid input");

        assert_eq!(result, expected);
    }
}