1use core::ffi::c_char;
2use std::fmt;
3
4use serde::Deserialize;
5
6use crate::ffi;
7use crate::private::take_string;
8
9#[derive(Debug, Clone)]
10pub enum StoreKitError {
11 InvalidArgument(String),
12 TimedOut(String),
13 NotSupported(String),
14 Framework(StoreKitFrameworkError),
15 Verification(VerificationFailure),
16 Unknown(String),
17}
18
19impl fmt::Display for StoreKitError {
20 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
21 match self {
22 Self::InvalidArgument(message)
23 | Self::TimedOut(message)
24 | Self::NotSupported(message)
25 | Self::Unknown(message) => formatter.write_str(message),
26 Self::Framework(error) => write!(
27 formatter,
28 "{} (domain={}, code={})",
29 error.localized_description, error.domain, error.code
30 ),
31 Self::Verification(error) => write!(
32 formatter,
33 "{} ({})",
34 error.localized_description,
35 error.code.as_str()
36 ),
37 }
38 }
39}
40
41impl std::error::Error for StoreKitError {}
42
43#[derive(Debug, Clone, PartialEq, Eq)]
44pub struct StoreKitFrameworkError {
45 pub domain: String,
46 pub code: i64,
47 pub localized_description: String,
48}
49
50#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct VerificationFailure {
52 pub code: VerificationErrorCode,
53 pub localized_description: String,
54}
55
56#[derive(Debug, Clone, PartialEq, Eq)]
57pub enum VerificationErrorCode {
58 RevokedCertificate,
59 InvalidCertificateChain,
60 InvalidDeviceVerification,
61 InvalidEncoding,
62 InvalidSignature,
63 MissingRequiredProperties,
64 Unknown(String),
65}
66
67impl VerificationErrorCode {
68 pub fn as_str(&self) -> &str {
69 match self {
70 Self::RevokedCertificate => "revokedCertificate",
71 Self::InvalidCertificateChain => "invalidCertificateChain",
72 Self::InvalidDeviceVerification => "invalidDeviceVerification",
73 Self::InvalidEncoding => "invalidEncoding",
74 Self::InvalidSignature => "invalidSignature",
75 Self::MissingRequiredProperties => "missingRequiredProperties",
76 Self::Unknown(value) => value.as_str(),
77 }
78 }
79
80 pub(crate) fn from_raw(raw: String) -> Self {
81 match raw.as_str() {
82 "revokedCertificate" => Self::RevokedCertificate,
83 "invalidCertificateChain" => Self::InvalidCertificateChain,
84 "invalidDeviceVerification" => Self::InvalidDeviceVerification,
85 "invalidEncoding" => Self::InvalidEncoding,
86 "invalidSignature" => Self::InvalidSignature,
87 "missingRequiredProperties" => Self::MissingRequiredProperties,
88 _ => Self::Unknown(raw),
89 }
90 }
91}
92
93#[derive(Debug, Deserialize)]
94pub(crate) struct VerificationErrorPayload {
95 #[allow(dead_code)]
96 kind: String,
97 code: String,
98 #[serde(rename = "localizedDescription")]
99 localized_description: String,
100}
101
102impl VerificationFailure {
103 pub(crate) fn from_payload(payload: VerificationErrorPayload) -> Self {
104 Self {
105 code: VerificationErrorCode::from_raw(payload.code),
106 localized_description: payload.localized_description,
107 }
108 }
109}
110
111#[derive(Debug, Deserialize)]
112struct FrameworkErrorPayload {
113 #[allow(dead_code)]
114 kind: String,
115 domain: String,
116 code: i64,
117 #[serde(rename = "localizedDescription")]
118 localized_description: String,
119}
120
121pub(crate) unsafe fn from_swift(status: i32, err_msg: *mut c_char) -> StoreKitError {
122 let message = take_string(err_msg);
123 match status {
124 ffi::status::INVALID_ARGUMENT => StoreKitError::InvalidArgument(
125 message.unwrap_or_else(|| "StoreKit reported an invalid argument".to_owned()),
126 ),
127 ffi::status::TIMED_OUT => StoreKitError::TimedOut(
128 message.unwrap_or_else(|| "StoreKit operation timed out".to_owned()),
129 ),
130 ffi::status::NOT_SUPPORTED => StoreKitError::NotSupported(
131 message.unwrap_or_else(|| "StoreKit operation is not supported".to_owned()),
132 ),
133 ffi::status::FRAMEWORK_ERROR => parse_framework_error(message),
134 ffi::status::VERIFICATION_ERROR => parse_verification_error(message),
135 _ => StoreKitError::Unknown(
136 message
137 .unwrap_or_else(|| format!("StoreKit bridge returned unexpected status {status}")),
138 ),
139 }
140}
141
142fn parse_framework_error(message: Option<String>) -> StoreKitError {
143 message.map_or_else(
144 || {
145 StoreKitError::Framework(StoreKitFrameworkError {
146 domain: "StoreKit".to_owned(),
147 code: i64::from(ffi::status::FRAMEWORK_ERROR),
148 localized_description: "StoreKit framework error".to_owned(),
149 })
150 },
151 |json| {
152 serde_json::from_str::<FrameworkErrorPayload>(&json).map_or_else(
153 |_| {
154 StoreKitError::Framework(StoreKitFrameworkError {
155 domain: "StoreKit".to_owned(),
156 code: i64::from(ffi::status::FRAMEWORK_ERROR),
157 localized_description: json,
158 })
159 },
160 |payload| {
161 StoreKitError::Framework(StoreKitFrameworkError {
162 domain: payload.domain,
163 code: payload.code,
164 localized_description: payload.localized_description,
165 })
166 },
167 )
168 },
169 )
170}
171
172fn parse_verification_error(message: Option<String>) -> StoreKitError {
173 message.map_or_else(
174 || {
175 StoreKitError::Verification(VerificationFailure {
176 code: VerificationErrorCode::Unknown("unknown".to_owned()),
177 localized_description: "StoreKit verification failed".to_owned(),
178 })
179 },
180 |json| {
181 serde_json::from_str::<VerificationErrorPayload>(&json).map_or_else(
182 |_| {
183 StoreKitError::Verification(VerificationFailure {
184 code: VerificationErrorCode::Unknown("unknown".to_owned()),
185 localized_description: json,
186 })
187 },
188 |payload| StoreKitError::Verification(VerificationFailure::from_payload(payload)),
189 )
190 },
191 )
192}