use core::str::FromStr;
use derive_more::{Display, IsVariant};
use smol_str::SmolStr;
#[cfg_attr(
feature = "quickcheck",
derive(::quickcheck_richderive::Arbitrary),
quickcheck(arbitrary = "crate::quickcheck_helpers::strings::channel_layout")
)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Display, IsVariant)]
#[display("{}", self.as_str())]
#[non_exhaustive]
pub enum ChannelLayout {
Mono,
Stereo,
N2Point1,
N3Point0,
N3Point0Back,
N3Point1,
Quad,
N5Point0,
N5Point0Back,
N5Point1,
N5Point1Back,
N6Point0,
N6Point1,
N7Point0,
N7Point1,
Hexagonal,
Octagonal,
Ambisonic1,
Ambisonic2,
Ambisonic3,
Other(SmolStr),
}
impl Default for ChannelLayout {
#[inline]
fn default() -> Self {
Self::Other(SmolStr::new_inline(""))
}
}
impl ChannelLayout {
pub fn as_str(&self) -> &str {
match self {
Self::Mono => "mono",
Self::Stereo => "stereo",
Self::N2Point1 => "2.1",
Self::N3Point0 => "3.0",
Self::N3Point0Back => "3.0(back)",
Self::N3Point1 => "3.1",
Self::Quad => "quad",
Self::N5Point0 => "5.0",
Self::N5Point0Back => "5.0(side)",
Self::N5Point1 => "5.1",
Self::N5Point1Back => "5.1(side)",
Self::N6Point0 => "6.0",
Self::N6Point1 => "6.1",
Self::N7Point0 => "7.0",
Self::N7Point1 => "7.1",
Self::Hexagonal => "hexagonal",
Self::Octagonal => "octagonal",
Self::Ambisonic1 => "ambisonic1",
Self::Ambisonic2 => "ambisonic2",
Self::Ambisonic3 => "ambisonic3",
Self::Other(s) => s.as_str(),
}
}
}
impl FromStr for ChannelLayout {
type Err = core::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"mono" => Self::Mono,
"stereo" => Self::Stereo,
"2.1" => Self::N2Point1,
"3.0" => Self::N3Point0,
"3.0(back)" => Self::N3Point0Back,
"3.1" => Self::N3Point1,
"quad" => Self::Quad,
"5.0" => Self::N5Point0,
"5.0(side)" => Self::N5Point0Back,
"5.1" => Self::N5Point1,
"5.1(side)" => Self::N5Point1Back,
"6.0" => Self::N6Point0,
"6.1" => Self::N6Point1,
"7.0" => Self::N7Point0,
"7.1" => Self::N7Point1,
"hexagonal" => Self::Hexagonal,
"octagonal" => Self::Octagonal,
"ambisonic1" => Self::Ambisonic1,
"ambisonic2" => Self::Ambisonic2,
"ambisonic3" => Self::Ambisonic3,
other => Self::Other(SmolStr::new(other)),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use ::std::string::ToString;
#[test]
fn every_named_variant_round_trips() {
for slug in [
"mono",
"stereo",
"2.1",
"3.0",
"3.0(back)",
"3.1",
"quad",
"5.0",
"5.0(side)",
"5.1",
"5.1(side)",
"6.0",
"6.1",
"7.0",
"7.1",
"hexagonal",
"octagonal",
"ambisonic1",
"ambisonic2",
"ambisonic3",
] {
let v: ChannelLayout = slug.parse().unwrap();
assert!(!v.is_other(), "`{slug}` should be a named variant");
assert_eq!(v.as_str(), slug, "round-trip mismatch for `{slug}`");
}
}
#[test]
fn unknown_layout_lands_in_other() {
let v: ChannelLayout = "22.2".parse().unwrap();
assert!(v.is_other());
assert_eq!(v.as_str(), "22.2");
assert_eq!(v.to_string(), "22.2");
}
#[test]
fn display_matches_as_str() {
assert_eq!(ChannelLayout::Stereo.to_string(), "stereo");
assert_eq!(ChannelLayout::N5Point1.to_string(), "5.1");
assert_eq!(
ChannelLayout::Other(SmolStr::new("custom_layout")).to_string(),
"custom_layout"
);
}
#[test]
fn is_variant_predicates() {
assert!(ChannelLayout::Mono.is_mono());
assert!(ChannelLayout::Stereo.is_stereo());
assert!(ChannelLayout::N5Point1.is_n_5_point_1());
assert!(ChannelLayout::Other(SmolStr::new("x")).is_other());
}
}