use thiserror::Error; #[derive(Error, Debug)] pub enum ControlPointError { // Utiliser par les DeviceItems dans leur conversion vers un media renderer #[error("{0} is not a MediaRenderer")] IsNotAMediaRender(String), #[error("{0} is not a MediaServer")] IsNotAMediaServer(String), #[error("Cannot build MusicRendererBackend for {0}")] MusicRendererBackendBuild(String), #[error("{0}")] ParsingError(String), #[error("OpenHome Error: {0}")] OpenHomeError(String), #[error("OpenHome Product source list does not expose a Playlist entry")] OpenHomeNoPlaylistEntry(), #[error("MusicRendererBackend {0} exposes no OpenHome services")] OpenHomeNotAValidDevice(String), #[error("UpnpError Error: {0}")] UpnpError(String), #[error("MusicRenderer operation '{0}' is not supported by backend '{1}'")] UpnpOperationNotSupported(String, String), #[error("Missing {0} element in SOAP body")] UpnpMissingReturnValue(String), #[error("Invalid {0} value: {1}")] UpnpBadReturnValue(String, String), #[error("Soap Error: Upnp action call {0}")] SoapAction(String), #[error("{0} returned UPnP error {1}: {2} (HTTP status {3})")] SoapUpnpParseError(String, u32, String, u32), #[error("{0} failed with HTTP status {1} and body: {2}")] SoapActionWrongBody(String, u32, String), #[error("Soap Error: No envelop for action {0}")] SoapNoEnvelop(String), #[error("ArilycTcp Error: {0}")] ArilycTcpError(String), #[error("LinkPlay Error: {0}")] LinkPlayError(String), #[error("Chromecast Error: {0}")] ChromecastError(String), #[error("MediaServer Error: {0}")] MediaServerError(String), #[error("Queue Error: {0}")] QueueError(String), #[error("Invalid time format: {0}")] InvalidTimeFormat(String), #[error("Error on snapshot: {0}")] SnapshotError(String), #[error("Error on ControlPoint: {0}")] ControlPoint(String), #[error("Queue sync cancelled (superseded by a newer request)")] SyncCancelled, } impl ControlPointError { pub fn upnp_operation_not_supported(operation: &str, service: &str) -> Self { ControlPointError::UpnpOperationNotSupported(operation.to_string(), service.to_string()) } /// Returns true if the error is a transient transport-level failure that /// may succeed on retry (TCP refused, connection reset, timeout). /// /// Protocol-level errors (UPnP fault, HTTP 4xx/5xx with a valid body) /// are **not** transient: the device understood the request and rejected it. pub fn is_transient_soap_error(&self) -> bool { matches!(self, ControlPointError::SoapAction(_)) } pub fn upnp_missing_return_value(value: &str) -> Self { ControlPointError::UpnpMissingReturnValue(value.to_string()) } pub fn upnp_bad_return_value(name: &str, value: &str) -> Self { ControlPointError::UpnpBadReturnValue(name.to_string(), value.to_string()) } pub fn arilyc_tcp_error(message: &str) -> Self { ControlPointError::ArilycTcpError(message.to_string()) } }