| // Copyright 2023 Google LLC | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | pub mod codec_capabilities; | 
 |  | 
 | pub mod metadata_ltv; | 
 |  | 
 | use crate::{codable_as_bitmask, decodable_enum}; | 
 |  | 
 | // Source: | 
 | // https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/profiles_and_services/generic_audio/context_type.yaml | 
 | decodable_enum! { | 
 |     pub enum ContextType<u16, crate::packet_encoding::Error, OutOfRange> { | 
 |         Unspecified = 0x0001, | 
 |         Conversational = 0x0002, | 
 |         Media = 0x0004, | 
 |         Game = 0x0008, | 
 |         Instructional = 0x0010, | 
 |         VoiceAssistants = 0x0020, | 
 |         Live = 0x0040, | 
 |         SoundEffects = 0x0080, | 
 |         Notifications = 0x0100, | 
 |         Ringtone = 0x0200, | 
 |         Alerts = 0x0400, | 
 |         EmergencyAlarm = 0x0800, | 
 |     } | 
 | } | 
 |  | 
 | codable_as_bitmask!(ContextType, u16); | 
 |  | 
 | // Source: | 
 | // https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/profiles_and_services/generic_audio/audio_location_definitions.yaml | 
 | // Regexp magic for quick variants: | 
 | // %s/ - value: \(\S\+\)\n   audio_location: \(.*\)\n/\2 = \1\r,/g | 
 | // with subsequent removal of Spaces | 
 | decodable_enum! { | 
 |     pub enum AudioLocation<u32, crate::packet_encoding::Error, OutOfRange> { | 
 |         FrontLeft = 0x00000001, | 
 |         FrontRight = 0x00000002, | 
 |         FrontCenter = 0x00000004, | 
 |         LowFrequencyEffects1 = 0x00000008, | 
 |         BackLeft = 0x00000010, | 
 |         BackRight = 0x00000020, | 
 |         FrontLeftOfCenter = 0x00000040, | 
 |         FrontRightOfCenter = 0x00000080, | 
 |         BackCenter = 0x00000100, | 
 |         LowFrequencyEffects2 = 0x00000200, | 
 |         SideLeft = 0x00000400, | 
 |         SideRight = 0x00000800, | 
 |         TopFrontLeft = 0x00001000, | 
 |         TopFrontRight = 0x00002000, | 
 |         TopFrontCenter = 0x00004000, | 
 |         TopCenter = 0x00008000, | 
 |         TopBackLeft = 0x00010000, | 
 |         TopBackRight = 0x00020000, | 
 |         TopSideLeft = 0x00040000, | 
 |         TopSideRight = 0x00080000, | 
 |         TopBackCenter = 0x00100000, | 
 |         BottomFrontCenter = 0x00200000, | 
 |         BottomFrontLeft = 0x00400000, | 
 |         BottomFrontRight = 0x00800000, | 
 |         FrontLeftWide = 0x01000000, | 
 |         FrontRightWide = 0x02000000, | 
 |         LeftSurround = 0x04000000, | 
 |         RightSurround = 0x08000000, | 
 |     } | 
 | } | 
 |  | 
 | codable_as_bitmask!(AudioLocation, u32); | 
 |  | 
 | #[cfg(test)] | 
 | mod tests { | 
 |     use super::*; | 
 |  | 
 |     #[test] | 
 |     fn locations_decodable() { | 
 |         let five_point_one = 0b111111; | 
 |  | 
 |         let locations: std::collections::HashSet<AudioLocation> = | 
 |             AudioLocation::from_bits(five_point_one).collect(); | 
 |  | 
 |         assert_eq!(6, locations.len()); | 
 |  | 
 |         let expected_locations = [ | 
 |             AudioLocation::FrontLeft, | 
 |             AudioLocation::FrontRight, | 
 |             AudioLocation::FrontCenter, | 
 |             AudioLocation::LowFrequencyEffects1, | 
 |             AudioLocation::BackLeft, | 
 |             AudioLocation::BackRight, | 
 |         ] | 
 |         .into_iter() | 
 |         .collect(); | 
 |  | 
 |         assert_eq!(locations, expected_locations); | 
 |  | 
 |         assert_eq!(AudioLocation::try_from(0x4), Ok(AudioLocation::FrontCenter)); | 
 |  | 
 |         // Directly decoding a location that is not a single bit is an error. | 
 |         assert!(AudioLocation::try_from(0b1010101).is_err()); | 
 |     } | 
 |  | 
 |     #[test] | 
 |     fn locations_encodable() { | 
 |         let locations_missing_sub = [ | 
 |             AudioLocation::FrontLeft, | 
 |             AudioLocation::FrontRight, | 
 |             AudioLocation::FrontCenter, | 
 |             AudioLocation::BackLeft, | 
 |             AudioLocation::BackRight, | 
 |         ]; | 
 |  | 
 |         let value = AudioLocation::to_bits(locations_missing_sub.iter()); | 
 |  | 
 |         assert_eq!(0b110111, value); | 
 |     } | 
 |  | 
 |     #[test] | 
 |     fn context_type_decodable() { | 
 |         let contexts: Vec<ContextType> = ContextType::from_bits(0b10).collect(); | 
 |         assert_eq!(contexts.len(), 1); | 
 |         assert_eq!(contexts[0], ContextType::Conversational); | 
 |  | 
 |         let live_and_instructional = 0b1010000; | 
 |         let contexts: std::collections::HashSet<ContextType> = | 
 |             ContextType::from_bits(live_and_instructional).collect(); | 
 |  | 
 |         assert_eq!(contexts.len(), 2); | 
 |         assert_eq!(contexts, [ContextType::Live, ContextType::Instructional].into_iter().collect()); | 
 |  | 
 |         let alerts_and_conversational = 0x0402; | 
 |  | 
 |         let contexts: std::collections::HashSet<ContextType> = | 
 |             ContextType::from_bits(alerts_and_conversational).collect(); | 
 |  | 
 |         assert_eq!(contexts.len(), 2); | 
 |         assert_eq!( | 
 |             contexts, | 
 |             [ContextType::Alerts, ContextType::Conversational].into_iter().collect() | 
 |         ); | 
 |     } | 
 |  | 
 |     #[test] | 
 |     fn context_type_encodable() { | 
 |         let contexts = [ContextType::Notifications, ContextType::SoundEffects, ContextType::Game]; | 
 |  | 
 |         let value = ContextType::to_bits(contexts.iter()); | 
 |  | 
 |         assert_eq!(0x188, value); | 
 |     } | 
 | } |