rusty_sonos/
errors.rs

1//! Crate error types
2
3/// An XML-related error
4#[derive(Debug)]
5pub enum XMLError {
6    /// Parsing error
7    ParseError(roxmltree::Error),
8    /// Element not found in XML
9    ElementNotFound(String),
10    /// An unexpected value was found while processing XML
11    UnexpectedValue(String), // this string contains the label and value for the unexpected value
12    /// An error occurred while building XML
13    XMLBuilderError(xml_builder::XMLError),
14}
15
16impl From<xml_builder::XMLError> for XMLError {
17    fn from(error: xml_builder::XMLError) -> Self {
18        Self::XMLBuilderError(error)
19    }
20}
21
22impl From<roxmltree::Error> for XMLError {
23    fn from(error: roxmltree::Error) -> Self {
24        Self::ParseError(error)
25    }
26}
27
28impl std::fmt::Display for XMLError {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        match self {
31            Self::ParseError(source) => write!(f, "error parsing XML: {}", source),
32            Self::ElementNotFound(details) => write!(f, "element not found {}", details),
33            Self::UnexpectedValue(details) => write!(f, "unexpected value: {}", details),
34            Self::XMLBuilderError(source) => write!(f, "error building XML: {}", source),
35        }
36    }
37}
38
39impl std::error::Error for XMLError {
40    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
41        match self {
42            Self::ElementNotFound(_) => None,
43            Self::ParseError(source) => Some(source),
44            Self::UnexpectedValue(_) => None,
45            Self::XMLBuilderError(source) => Some(source),
46        }
47    }
48}
49
50/// Represents an error involving a UDP socket
51pub type UDPError = std::io::Error;
52
53/// Errors that may be returned from speaker methods
54#[derive(Debug)]
55pub enum SpeakerError {
56    /// An error that occurred while making a request to the speaker
57    RequestError(reqwest::Error),
58    /// An error that occurred while processing the response from the speaker
59    ResponseError(reqwest::Error),
60    /// An XML-related error
61    XMLError(XMLError),
62    /// Bad input for a speaker action, with the string containing additional details
63    InvalidInput(String),
64    /// A speaker-specific error
65    SonosError(SonosError),
66}
67
68impl From<XMLError> for SpeakerError {
69    fn from(error: XMLError) -> Self {
70        Self::XMLError(error)
71    }
72}
73
74impl From<SonosError> for SpeakerError {
75    fn from(error: SonosError) -> Self {
76        Self::SonosError(error)
77    }
78}
79
80impl From<reqwest::Error> for SpeakerError {
81    fn from(error: reqwest::Error) -> Self {
82        if error.is_builder()
83            || error.is_connect()
84            || error.is_redirect()
85            || error.is_timeout()
86            || error.is_request()
87        {
88            Self::RequestError(error)
89        } else {
90            Self::ResponseError(error)
91        }
92    }
93}
94
95impl std::fmt::Display for SpeakerError {
96    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97        match self {
98            Self::InvalidInput(details) => write!(f, "invalid input: {}", details),
99            Self::RequestError(source) => write!(f, "request error: {}", source),
100            Self::ResponseError(source) => write!(f, "response error: {}", source),
101            Self::SonosError(source) => write!(f, "Sonos speaker error: {}", source),
102            Self::XMLError(source) => write!(f, "XML error: {}", source),
103        }
104    }
105}
106
107impl std::error::Error for SpeakerError {
108    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
109        match self {
110            Self::InvalidInput(_) => None,
111            Self::RequestError(source) => Some(source),
112            Self::ResponseError(source) => Some(source),
113            Self::SonosError(source) => Some(source),
114            Self::XMLError(source) => Some(source),
115        }
116    }
117}
118
119/// Speaker-specific errors
120#[derive(Debug)]
121pub enum SonosError {
122    /// Not able to change transition, ex. pausing when playback is already paused
123    TransitionUnavailable,
124    /// Invalid target for operations such as seek (ex. an invalid duration) or next (using next at the end of the queue, or while not in a queue)
125    InvalidSeekTarget,
126    /// Some other Sonos error, with the string containing additional data
127    Unknown(String),
128}
129
130impl SonosError {
131    pub(crate) fn from_err_code(err_code: &str, additional_details: &str) -> Self {
132        match err_code {
133            "701" => Self::TransitionUnavailable,
134            "711" => Self::InvalidSeekTarget,
135            _ => Self::Unknown(String::from(additional_details)),
136        }
137    }
138}
139
140impl std::fmt::Display for SonosError {
141    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142        match self {
143            Self::TransitionUnavailable => write!(f, "transition unavailable"),
144            Self::InvalidSeekTarget => write!(f, "invalid seek target"),
145            Self::Unknown(s) => write!(f, "other Sonos error: {}", s),
146        }
147    }
148}
149
150impl std::error::Error for SonosError {}