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 {}