1use crate::error::OaatError;
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
7pub struct Capabilities {
8 pub pcm_max_rate_khz: u32,
9 pub pcm_max_bits: u8,
10 pub dsd_max_multiplier: Option<u16>,
11 pub flac: bool,
12 pub opus: bool,
13}
14
15impl Capabilities {
16 pub fn parse(s: &str) -> Result<Self, OaatError> {
17 let mut pcm_max_rate_khz = 0u32;
18 let mut pcm_max_bits = 0u8;
19 let mut dsd_max_multiplier = None;
20 let mut flac = false;
21 let mut opus = false;
22
23 for part in s.split(',') {
24 let part = part.trim();
25 if let Some(pcm) = part.strip_prefix("pcm:") {
26 let (rate_s, bits_s) = pcm
27 .split_once('/')
28 .ok_or_else(|| OaatError::InvalidCapabilityString(s.to_owned()))?;
29 pcm_max_rate_khz = rate_s
30 .parse()
31 .map_err(|_| OaatError::InvalidCapabilityString(s.to_owned()))?;
32 pcm_max_bits = bits_s
33 .parse()
34 .map_err(|_| OaatError::InvalidCapabilityString(s.to_owned()))?;
35 } else if let Some(dsd) = part.strip_prefix("dsd:") {
36 dsd_max_multiplier = Some(
37 dsd.parse()
38 .map_err(|_| OaatError::InvalidCapabilityString(s.to_owned()))?,
39 );
40 } else if part == "flac" {
41 flac = true;
42 } else if part == "opus" {
43 opus = true;
44 }
45 }
46
47 if pcm_max_rate_khz == 0 {
48 return Err(OaatError::InvalidCapabilityString(s.to_owned()));
49 }
50
51 Ok(Self {
52 pcm_max_rate_khz,
53 pcm_max_bits,
54 dsd_max_multiplier,
55 flac,
56 opus,
57 })
58 }
59
60 pub fn pcm_max_rate_hz(&self) -> u32 {
61 self.pcm_max_rate_khz * 1000
62 }
63}
64
65impl std::fmt::Display for Capabilities {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 write!(f, "pcm:{}/{}", self.pcm_max_rate_khz, self.pcm_max_bits)?;
68 if let Some(dsd) = self.dsd_max_multiplier {
69 write!(f, ",dsd:{dsd}")?;
70 }
71 if self.flac {
72 write!(f, ",flac")?;
73 }
74 if self.opus {
75 write!(f, ",opus")?;
76 }
77 Ok(())
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn parse_pcm_only() {
87 let caps = Capabilities::parse("pcm:192/24").unwrap();
88 assert_eq!(caps.pcm_max_rate_khz, 192);
89 assert_eq!(caps.pcm_max_bits, 24);
90 assert_eq!(caps.dsd_max_multiplier, None);
91 assert!(!caps.flac);
92 }
93
94 #[test]
95 fn parse_full() {
96 let caps = Capabilities::parse("pcm:768/32,dsd:256,flac,opus").unwrap();
97 assert_eq!(caps.pcm_max_rate_khz, 768);
98 assert_eq!(caps.pcm_max_bits, 32);
99 assert_eq!(caps.dsd_max_multiplier, Some(256));
100 assert!(caps.flac);
101 assert!(caps.opus);
102 }
103
104 #[test]
105 fn roundtrip() {
106 let original = "pcm:768/32,dsd:256,flac";
107 let caps = Capabilities::parse(original).unwrap();
108 assert_eq!(caps.to_string(), original);
109 }
110
111 #[test]
112 fn invalid() {
113 assert!(Capabilities::parse("dsd:256").is_err());
114 assert!(Capabilities::parse("garbage").is_err());
115 }
116}