Skip to main content

weatherkit/
error.rs

1use core::fmt;
2
3use serde::{Deserialize, Serialize};
4
5use crate::ffi;
6use crate::private::parse_json_from_static;
7
8pub const WEATHERKIT_BRIDGE_ERROR_DOMAIN: &str = "WeatherKitBridge";
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct WeatherKitError {
12    pub domain: String,
13    pub code: i64,
14    pub message: String,
15}
16
17#[derive(Debug, Clone, PartialEq, Eq)]
18#[non_exhaustive]
19pub enum WeatherError {
20    PermissionDenied,
21    Unknown(String),
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
25#[serde(rename_all = "camelCase")]
26pub struct WeatherErrorDescriptor {
27    pub raw_value: String,
28    pub error_description: Option<String>,
29    pub failure_reason: Option<String>,
30    pub help_anchor: Option<String>,
31    pub recovery_suggestion: Option<String>,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
35#[serde(rename_all = "camelCase")]
36pub(crate) struct ErrorPayload {
37    pub domain: String,
38    pub code: i64,
39    pub message: String,
40}
41
42impl WeatherError {
43    pub fn raw_value(&self) -> &str {
44        match self {
45            Self::PermissionDenied => "permissionDenied",
46            Self::Unknown(value) => value.as_str(),
47        }
48    }
49
50    pub fn descriptors() -> Result<Vec<WeatherErrorDescriptor>, WeatherKitError> {
51        parse_json_from_static(
52            ffi::error::wk_weather_error_copy_descriptors_json,
53            "weather error descriptors",
54        )
55    }
56}
57
58impl WeatherKitError {
59    pub(crate) fn from_payload(payload: ErrorPayload) -> Self {
60        Self {
61            domain: payload.domain,
62            code: payload.code,
63            message: payload.message,
64        }
65    }
66
67    pub(crate) fn bridge(code: i64, message: impl Into<String>) -> Self {
68        Self {
69            domain: WEATHERKIT_BRIDGE_ERROR_DOMAIN.into(),
70            code,
71            message: message.into(),
72        }
73    }
74
75    pub fn is_entitlement_issue(&self) -> bool {
76        let domain = self.domain.to_ascii_lowercase();
77        let message = self.message.to_ascii_lowercase();
78        message.contains("entitlement")
79            || message.contains("permission")
80            || message.contains("bundle")
81            || message.contains("not authorized")
82            || message.contains("denied")
83            || domain.contains("jwtauthenticator")
84            || domain.contains("weatherdaemon")
85            || domain.contains("auth")
86    }
87}
88
89impl fmt::Display for WeatherKitError {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        write!(f, "{} ({}) [{}]", self.message, self.code, self.domain)
92    }
93}
94
95impl std::error::Error for WeatherKitError {}