Skip to main content

sonos_api/
types.rs

1//! Common types shared across the sonos-sdk workspace
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5
6/// Macro to generate common ID type implementations
7macro_rules! impl_id_type {
8    ($name:ident) => {
9        impl fmt::Display for $name {
10            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
11                write!(f, "{}", self.0)
12            }
13        }
14
15        impl From<&str> for $name {
16            fn from(s: &str) -> Self {
17                $name::new(s)
18            }
19        }
20
21        impl From<String> for $name {
22            fn from(s: String) -> Self {
23                $name::new(s)
24            }
25        }
26    };
27}
28
29/// Unique identifier for a Sonos speaker
30///
31/// Typically the UUID from the UPnP device description,
32/// normalized to strip the "uuid:" prefix if present.
33#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
34pub struct SpeakerId(String);
35
36impl SpeakerId {
37    /// Creates a new SpeakerId, normalizing the format
38    pub fn new(id: impl Into<String>) -> Self {
39        let id = id.into();
40        let normalized = id.strip_prefix("uuid:").unwrap_or(&id);
41        Self(normalized.to_string())
42    }
43
44    pub fn as_str(&self) -> &str {
45        &self.0
46    }
47}
48
49impl_id_type!(SpeakerId);
50
51/// Unique identifier for a zone group
52///
53/// Typically has the format "RINCON_xxxxx:n".
54#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
55pub struct GroupId(String);
56
57impl GroupId {
58    pub fn new(id: impl Into<String>) -> Self {
59        Self(id.into())
60    }
61
62    pub fn as_str(&self) -> &str {
63        &self.0
64    }
65}
66
67impl_id_type!(GroupId);
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn test_speaker_id_strips_uuid_prefix() {
75        let id = SpeakerId::new("uuid:RINCON_123456789");
76        assert_eq!(id.as_str(), "RINCON_123456789");
77    }
78
79    #[test]
80    fn test_speaker_id_without_prefix() {
81        let id = SpeakerId::new("RINCON_123456789");
82        assert_eq!(id.as_str(), "RINCON_123456789");
83    }
84
85    #[test]
86    fn test_speaker_id_equality() {
87        let id1 = SpeakerId::new("uuid:RINCON_123");
88        let id2 = SpeakerId::new("RINCON_123");
89        assert_eq!(id1, id2);
90    }
91
92    #[test]
93    fn test_group_id() {
94        let id = GroupId::new("RINCON_123:0");
95        assert_eq!(id.as_str(), "RINCON_123:0");
96    }
97
98    #[test]
99    fn test_display() {
100        assert_eq!(format!("{}", SpeakerId::new("RINCON_123")), "RINCON_123");
101        assert_eq!(format!("{}", GroupId::new("RINCON_123:0")), "RINCON_123:0");
102    }
103}