use crate::{MediaType, SerieID};
use eyre::ensure;
use std::str::FromStr;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Target {
pub id: SerieID,
pub kind: MediaType,
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);
}
}