embedded_wg_display/widget/manager/
mod.rs1use alloc::string::String;
3use alloc::vec::Vec;
4use common::models::SystemConfiguration;
5
6use crate::runtime::Runtime;
7use crate::runtime::http_sync::{self};
8use crate::storage::StorageError;
9use crate::util::globals;
10use defmt::error;
11
12#[derive(Debug, defmt::Format)]
14pub enum WidgetManagerError {
15 Storage(StorageError),
17 HttpError(&'static str),
19 WasmError(&'static str),
21 AlreadyInstalled(&'static str),
22}
23
24impl From<StorageError> for WidgetManagerError {
25 fn from(e: StorageError) -> Self {
26 WidgetManagerError::Storage(e)
27 }
28}
29
30impl From<&'static str> for WidgetManagerError {
31 fn from(e: &'static str) -> Self {
32 WidgetManagerError::HttpError(e)
33 }
34}
35
36impl From<reqwless::Error> for WidgetManagerError {
37 fn from(_: reqwless::Error) -> Self {
38 WidgetManagerError::HttpError("HTTP request failed")
39 }
40}
41
42pub struct WidgetManager;
44
45impl WidgetManager {
46 pub async fn install_widget(
54 download_url: &str,
55 description: &str,
56 ) -> Result<(), WidgetManagerError> {
57 let response = http_sync::http_request_async(http_sync::HttpRequest {
59 method: reqwless::request::Method::GET,
60 url: alloc::string::String::from(download_url),
61 body: None,
62 })
63 .await
64 .map_err(|_| WidgetManagerError::HttpError("HTTP bridge request failed"))?;
65
66 let mut runtime = Runtime::new();
67
68 let widget_metadata_result = unsafe { runtime.get_widget_metadata(&response.bytes).await };
69
70 let mut widget_metadata = match widget_metadata_result {
71 Ok(config) => config,
72 Err(_) => {
73 error!("Failed to get config schema for '{}'", download_url);
74 return Err(WidgetManagerError::WasmError(
75 "Failed to get widget config schema",
76 ));
77 }
78 };
79
80 let system_config: SystemConfiguration =
82 globals::with_storage(|storage| storage.get_system_config())
83 .await
84 .unwrap_or_default();
85
86 if system_config
87 .widgets
88 .iter()
89 .any(|w| w.name == widget_metadata.name)
90 {
91 return Err(WidgetManagerError::AlreadyInstalled(
92 "Widget with same name has already been installed",
93 ));
94 }
95
96 widget_metadata.description = String::from(description);
97
98 globals::with_storage(|storage| {
100 storage.save_compiled_widget(widget_metadata, &response.bytes)
101 })
102 .await?;
103 Ok(())
104 }
105
106 pub async fn deinstall_widget(widget_name: &str) -> Result<(), WidgetManagerError> {
112 globals::with_storage(|storage| storage.deinstall_widget(widget_name)).await?;
113 Ok(())
114 }
115
116 #[allow(dead_code)]
123 pub async fn get_widget(widget_name: &str) -> Result<Vec<u8>, WidgetManagerError> {
124 let widget = globals::with_storage(|storage| storage.wasm_read(widget_name)).await?;
125 Ok(widget)
126 }
127
128 #[allow(dead_code)]
133 pub async fn get_widgets() -> Result<Vec<String>, WidgetManagerError> {
134 let widget_names = globals::with_storage(|storage| {
135 storage
136 .get_system_config()
137 .map(|config| config.widgets.into_iter().map(|w| w.name).collect())
138 })
139 .await?;
140 Ok(widget_names)
141 }
142}