1use lingxia_platform::PlatformError;
2use lingxia_webview::WebViewError;
3#[cfg(feature = "js-appservice")]
4use rong::RongJSError;
5#[cfg(feature = "js-appservice")]
6use rong::error::{ErrorData, ErrorNumber};
7use serde_json::Value;
8use std::io;
9use thiserror::Error;
10
11#[derive(Debug, Clone, Error)]
12pub enum LxAppError {
13 #[error("WebView error: {0}")]
15 WebView(String),
16
17 #[error("{0} not found")]
18 ResourceNotFound(String),
19
20 #[error("{0} is not valid JSON file")]
21 InvalidJsonFile(String),
22
23 #[error("Invalid parameter: {0}")]
25 InvalidParameter(String),
26
27 #[error("Unsupported operation: {0}")]
29 UnsupportedOperation(String),
30
31 #[error("Surface conflict: {0}")]
33 SurfaceConflict(String),
34
35 #[error("I/O error: {0}")]
37 IoError(String),
38
39 #[error("Runtime error: {0}")]
41 Runtime(String),
42
43 #[error("Channel error: {0}")]
45 ChannelError(String),
46
47 #[error("Resource exhausted: {0}")]
49 ResourceExhausted(String),
50
51 #[error("Bridge error: {0}")]
53 Bridge(String),
54
55 #[error("Rong Error: {0}")]
57 RongJS(String),
58
59 #[error("{code}: {message}")]
61 RongJSHost {
62 code: String,
63 message: String,
64 data: Option<Value>,
65 },
66
67 #[error("Plugin not configured: {0}")]
69 PluginNotConfigured(String),
70
71 #[error("Plugin download failed: {0}")]
73 PluginDownloadFailed(String),
74}
75
76impl From<io::Error> for LxAppError {
77 fn from(error: io::Error) -> Self {
78 LxAppError::IoError(error.to_string())
79 }
80}
81
82impl<T> From<std::sync::mpsc::SendError<T>> for LxAppError {
83 fn from(error: std::sync::mpsc::SendError<T>) -> Self {
84 LxAppError::ChannelError(error.to_string())
85 }
86}
87
88impl From<serde_json::Error> for LxAppError {
89 fn from(error: serde_json::Error) -> Self {
90 LxAppError::Bridge(format!("JSON Processing Error: {}", error))
91 }
92}
93
94#[cfg(feature = "js-appservice")]
95impl From<RongJSError> for LxAppError {
96 fn from(error: RongJSError) -> Self {
97 if let Some(host) = error.as_host_error() {
98 let data = host.data.as_ref().map(error_data_to_json);
99 return LxAppError::RongJSHost {
100 code: host.code.to_string(),
101 message: host.message.clone(),
102 data,
103 };
104 }
105 LxAppError::RongJS(error.to_string())
106 }
107}
108
109impl From<PlatformError> for LxAppError {
110 fn from(error: PlatformError) -> Self {
111 match error {
112 PlatformError::NotSupported(message) => LxAppError::UnsupportedOperation(message),
113 PlatformError::InvalidParameter(message) => LxAppError::InvalidParameter(message),
114 PlatformError::AssetNotFound(message) => LxAppError::ResourceNotFound(message),
115 other => LxAppError::Runtime(other.to_string()),
116 }
117 }
118}
119
120impl From<WebViewError> for LxAppError {
121 fn from(error: WebViewError) -> Self {
122 match error {
123 WebViewError::WebView(detail) => LxAppError::WebView(detail),
124 other => LxAppError::WebView(other.to_string()),
125 }
126 }
127}
128
129impl From<lingxia_update::UpdateError> for LxAppError {
130 fn from(error: lingxia_update::UpdateError) -> Self {
131 match error {
132 lingxia_update::UpdateError::InvalidParameter(detail) => {
133 LxAppError::InvalidParameter(detail)
134 }
135 lingxia_update::UpdateError::UnsupportedOperation(detail) => {
136 LxAppError::UnsupportedOperation(detail)
137 }
138 lingxia_update::UpdateError::RequiresRuntimeUpgrade(detail) => {
139 LxAppError::requires_runtime_upgrade(detail)
140 }
141 lingxia_update::UpdateError::ResourceNotFound(detail) => {
142 LxAppError::ResourceNotFound(detail)
143 }
144 lingxia_update::UpdateError::Io(detail) => LxAppError::IoError(detail),
145 lingxia_update::UpdateError::Runtime(detail) => LxAppError::Runtime(detail),
146 }
147 }
148}
149
150impl From<lingxia_settings::SettingsError> for LxAppError {
151 fn from(error: lingxia_settings::SettingsError) -> Self {
152 LxAppError::Runtime(error.to_string())
153 }
154}
155
156#[cfg(feature = "js-appservice")]
157fn error_data_to_json(data: &ErrorData) -> Value {
158 match data {
159 ErrorData::Null => Value::Null,
160 ErrorData::Bool(v) => Value::Bool(*v),
161 ErrorData::String(v) => Value::String(v.clone()),
162 ErrorData::Number(n) => match n {
163 ErrorNumber::I64(v) => Value::Number(serde_json::Number::from(*v)),
164 ErrorNumber::U64(v) => Value::Number(serde_json::Number::from(*v)),
165 ErrorNumber::F64(bits) => {
166 let num = f64::from_bits(*bits);
167 match serde_json::Number::from_f64(num) {
168 Some(value) => Value::Number(value),
169 None => Value::String(num.to_string()),
170 }
171 }
172 },
173 ErrorData::Array(items) => Value::Array(items.iter().map(error_data_to_json).collect()),
174 ErrorData::Object(obj) => Value::Object(
175 obj.iter()
176 .map(|(k, v)| (k.clone(), error_data_to_json(v)))
177 .collect(),
178 ),
179 }
180}
181
182const REQUIRES_RUNTIME_UPGRADE_CODE: &str = "6002";
183
184impl LxAppError {
185 pub fn requires_runtime_upgrade(detail: impl Into<String>) -> Self {
187 Self::RongJSHost {
188 code: REQUIRES_RUNTIME_UPGRADE_CODE.to_string(),
189 message: detail.into(),
190 data: None,
191 }
192 }
193
194 pub fn is_requires_runtime_upgrade(&self) -> bool {
195 matches!(self, Self::RongJSHost { code, .. } if code == REQUIRES_RUNTIME_UPGRADE_CODE)
196 }
197
198 pub fn detail(&self) -> Option<&str> {
207 match self {
208 Self::WebView(detail)
209 | Self::ResourceNotFound(detail)
210 | Self::InvalidJsonFile(detail)
211 | Self::InvalidParameter(detail)
212 | Self::UnsupportedOperation(detail)
213 | Self::IoError(detail)
214 | Self::Runtime(detail) => Some(detail),
215 _ => None,
216 }
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use super::LxAppError;
223 use lingxia_webview::WebViewError;
224
225 #[test]
226 fn raw_webview_errors_keep_the_source_label() {
227 assert_eq!(
228 LxAppError::WebView("WebView not ready".to_string()).to_string(),
229 "WebView error: WebView not ready"
230 );
231 }
232
233 #[test]
234 fn typed_webview_errors_have_one_source_label() {
235 let error = LxAppError::from(WebViewError::WebView("creation failed".to_string()));
236 assert_eq!(error.to_string(), "WebView error: creation failed");
237
238 let error = LxAppError::from(WebViewError::InvalidCreateOptions(
239 "missing tag".to_string(),
240 ));
241 assert_eq!(
242 error.to_string(),
243 "WebView error: Invalid WebView create options: missing tag"
244 );
245 }
246}