use std::fmt;
use std::num::{NonZero, ParseIntError, TryFromIntError};
use std::str::FromStr;
use crate::arguments::{ArgumentScanner, Arguments, ExpectArg, FromArgs};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum AudioRepetition {
Forever,
Count(NonZero<u32>),
}
impl Default for AudioRepetition {
fn default() -> Self {
Self::Count(NonZero::new(1).unwrap())
}
}
impl fmt::Display for AudioRepetition {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Forever => (-1).fmt(f),
Self::Count(amount) => amount.fmt(f),
}
}
}
impl FromStr for AudioRepetition {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "-1" {
return Ok(Self::Forever);
}
s.parse().map(Self::Count)
}
}
impl TryFrom<u32> for AudioRepetition {
type Error = TryFromIntError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
value.try_into().map(Self::Count)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Sound<S = String> {
pub fname: S,
pub volume: u8,
pub repeat: AudioRepetition,
pub priority: u8,
pub class: Option<S>,
pub url: Option<S>,
}
impl<S: Default> Default for Sound<S> {
fn default() -> Self {
Self {
fname: S::default(),
volume: 100,
repeat: AudioRepetition::default(),
priority: 50,
class: None,
url: None,
}
}
}
impl<S: AsRef<str>> Sound<S> {
pub fn is_off(&self) -> bool {
self.fname.as_ref().eq_ignore_ascii_case("off") && self.url.is_none()
}
}
impl<S> Sound<S> {
pub fn map_text<T, F>(self, mut f: F) -> Sound<T>
where
F: FnMut(S) -> T,
{
Sound {
fname: f(self.fname),
volume: self.volume,
repeat: self.repeat,
priority: self.priority,
class: self.class.map(&mut f),
url: self.url.map(f),
}
}
}
impl_into_owned!(Sound);
impl<S: AsRef<str>> Sound<S> {
pub fn borrow_text(&self) -> Sound<&str> {
Sound {
fname: self.fname.as_ref(),
volume: self.volume,
repeat: self.repeat,
class: self.class.as_ref().map(AsRef::as_ref),
url: self.url.as_ref().map(AsRef::as_ref),
priority: self.priority,
}
}
}
impl_partial_eq!(Sound);
impl<'a> Sound<&'a str> {
pub fn from_msp(source: &'a str) -> crate::Result<Self> {
Arguments::parse(source)?.into_scan().parse()
}
}
impl<'a, S: AsRef<str>> FromArgs<'a, S> for Sound<S> {
fn from_args<A: ArgumentScanner<'a, Decoded = S>>(mut scanner: A) -> crate::Result<Self> {
let fname = scanner.get_next_or("fname")?.expect_some("fname")?;
let volume = scanner.get_next_or("v")?.expect_number()?.unwrap_or(100);
let repeat = scanner
.get_next_or("l")?
.expect_number()?
.unwrap_or_default();
let priority = scanner.get_next_or("p")?.expect_number()?.unwrap_or(50);
let class = scanner.get_next_or("t")?;
let url = scanner.get_next_or("u")?;
scanner.expect_end()?;
Ok(Self {
fname,
volume,
repeat,
priority,
class,
url,
})
}
}
impl_from_str!(Sound);
impl<S: AsRef<str>> fmt::Display for Sound<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Sound {
fname,
volume,
repeat,
priority,
class,
url,
} = self.borrow_text();
crate::display::ElementFormatter {
name: "SOUND",
arguments: &[
&fname,
&(volume, 100),
&(repeat, AudioRepetition::default()),
&(priority, 50),
&class,
&url,
],
keywords: &[],
}
.fmt(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::{StringPair, format_from_pairs, parse_from_pairs};
const AUDIO_REPETITION_PAIRS: &[StringPair<AudioRepetition>] = &[
(AudioRepetition::Forever, "-1"),
(AudioRepetition::Count(NonZero::new(10).unwrap()), "10"),
];
#[test]
fn fmt_audio_repetition() {
let (actual, expected) = format_from_pairs(AUDIO_REPETITION_PAIRS);
assert_eq!(actual, expected);
}
#[test]
fn parse_audio_repetition() {
let (actual, expected) = parse_from_pairs(AUDIO_REPETITION_PAIRS);
assert_eq!(actual, expected);
}
#[test]
fn parse_audio_repetition_invalid() {
assert_eq!(
"0".parse::<AudioRepetition>(),
Err("0".parse::<NonZero<u32>>().unwrap_err())
);
}
}