Skip to main content

weatherkit/
error.rs

1//! WeatherKit error types.
2
3use core::fmt;
4
5use serde::{Deserialize, Serialize};
6
7use crate::ffi;
8use crate::private::parse_json_from_static;
9
10/// Names the bridge error domain used by the WeatherKit wrapper.
11pub const WEATHERKIT_BRIDGE_ERROR_DOMAIN: &str = "WeatherKitBridge";
12
13/// Represents an error returned by WeatherKit or the bridge layer.
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct WeatherKitError {
16    /// Matches the WeatherKit domain value.
17    pub domain: String,
18    /// Matches the WeatherKit code value.
19    pub code: i64,
20    /// Matches the WeatherKit message value.
21    pub message: String,
22}
23
24/// Represents a typed WeatherKit error case.
25#[derive(Debug, Clone, PartialEq, Eq)]
26#[non_exhaustive]
27pub enum WeatherError {
28    /// Matches the WeatherKit `PermissionDenied` case.
29    PermissionDenied,
30    /// Stores an unrecognized WeatherKit case name.
31    Unknown(String),
32}
33
34/// Represents the WeatherKit `WeatherErrorDescriptor` payload.
35#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
36#[serde(rename_all = "camelCase")]
37pub struct WeatherErrorDescriptor {
38    /// Matches the WeatherKit raw value value.
39    pub raw_value: String,
40    /// Matches the WeatherKit error description value.
41    pub error_description: Option<String>,
42    /// Matches the WeatherKit failure reason value.
43    pub failure_reason: Option<String>,
44    /// Matches the WeatherKit help anchor value.
45    pub help_anchor: Option<String>,
46    /// Matches the WeatherKit recovery suggestion value.
47    pub recovery_suggestion: Option<String>,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
51#[serde(rename_all = "camelCase")]
52pub(crate) struct ErrorPayload {
53    pub domain: String,
54    pub code: i64,
55    pub message: String,
56}
57
58impl WeatherError {
59    /// Returns the WeatherKit raw value for this case.
60    pub fn raw_value(&self) -> &str {
61        match self {
62            Self::PermissionDenied => "permissionDenied",
63            Self::Unknown(value) => value.as_str(),
64        }
65    }
66
67    /// Returns the WeatherKit descriptor catalog for this enum.
68    pub fn descriptors() -> Result<Vec<WeatherErrorDescriptor>, WeatherKitError> {
69        parse_json_from_static(
70            ffi::error::wk_weather_error_copy_descriptors_json,
71            "weather error descriptors",
72        )
73    }
74}
75
76impl WeatherKitError {
77    pub(crate) fn from_payload(payload: ErrorPayload) -> Self {
78        Self {
79            domain: payload.domain,
80            code: payload.code,
81            message: payload.message,
82        }
83    }
84
85    pub(crate) fn bridge(code: i64, message: impl Into<String>) -> Self {
86        Self {
87            domain: WEATHERKIT_BRIDGE_ERROR_DOMAIN.into(),
88            code,
89            message: message.into(),
90        }
91    }
92
93    /// Returns whether this WeatherKit error looks like an entitlement or authorization issue.
94    pub fn is_entitlement_issue(&self) -> bool {
95        let domain = self.domain.to_ascii_lowercase();
96        let message = self.message.to_ascii_lowercase();
97        message.contains("entitlement")
98            || message.contains("permission")
99            || message.contains("bundle")
100            || message.contains("not authorized")
101            || message.contains("denied")
102            || domain.contains("jwtauthenticator")
103            || domain.contains("weatherdaemon")
104            || domain.contains("auth")
105    }
106}
107
108impl fmt::Display for WeatherKitError {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        write!(f, "{} ({}) [{}]", self.message, self.code, self.domain)
111    }
112}
113
114impl std::error::Error for WeatherKitError {}