Skip to main content

slowshell_plugin_abi/
sdk.rs

1use std::{
2  any::Any,
3  collections::HashMap,
4  sync::{Mutex, OnceLock},
5};
6
7use abi_stable::{
8  sabi_trait::TD_Opaque,
9  std_types::{RBox, ROption, RResult, RStr, RString, RVec},
10};
11
12use crate::{
13  Args,
14  host::{Host, Host_TO},
15  payload::{PayloadBox, PayloadParser, PayloadParserBox},
16  surfaces::{
17    Component, Compositor, ConfigParser, ConfigParserBox, DeployableDesktopItem, DesktopItem,
18    ForegroundService, Payload, QuickSettings, Renderable, Service, Spotlight,
19    foreground_service_factory, service_factory,
20  },
21};
22
23pub use kdl;
24
25type ConfigValue = Box<dyn Any + Send + Sync>;
26
27pub(crate) fn config_values() -> &'static Mutex<HashMap<String, ConfigValue>> {
28  static VALUES: OnceLock<Mutex<HashMap<String, ConfigValue>>> = OnceLock::new();
29  VALUES.get_or_init(|| Mutex::new(HashMap::new()))
30}
31
32pub fn store_config_value<T: Send + Sync + 'static>(key: &str, value: T) {
33  config_values()
34    .lock()
35    .unwrap()
36    .insert(key.to_owned(), Box::new(value));
37}
38
39struct ParserFn<F> {
40  parse: F,
41}
42
43impl<F> ConfigParser for ParserFn<F>
44where
45  F: Fn(Option<&kdl::KdlNode>) -> Result<(), String> + Send + Sync + 'static,
46{
47  fn parse(&self, block: ROption<RStr<'_>>) -> RResult<(), RString> {
48    let node = match block {
49      ROption::RSome(text) => text
50        .parse::<kdl::KdlDocument>()
51        .ok()
52        .and_then(|document| document.nodes().first().cloned()),
53      ROption::RNone => None,
54    };
55
56    match (self.parse)(node.as_ref()) {
57      Ok(()) => RResult::ROk(()),
58      Err(message) => RResult::RErr(RString::from(message)),
59    }
60  }
61}
62
63pub fn config_parser<T: Send + Sync + 'static>(
64  name: &str,
65  parse: impl Fn(Option<&kdl::KdlNode>) -> T + Send + Sync + 'static,
66) -> ConfigParserBox {
67  let key = name.to_owned();
68  ConfigParserBox::from_value(
69    ParserFn {
70      parse: move |node: Option<&kdl::KdlNode>| {
71        let value = parse(node);
72        store_config_value(&key, value);
73        Ok(())
74      },
75    },
76    TD_Opaque,
77  )
78}
79
80pub fn config_parser_try<T: Send + Sync + 'static>(
81  name: &str,
82  parse: impl Fn(Option<&kdl::KdlNode>) -> Result<T, String> + Send + Sync + 'static,
83) -> ConfigParserBox {
84  let key = name.to_owned();
85  ConfigParserBox::from_value(
86    ParserFn {
87      parse: move |node: Option<&kdl::KdlNode>| {
88        let value = parse(node)?;
89        store_config_value(&key, value);
90        Ok(())
91      },
92    },
93    TD_Opaque,
94  )
95}
96
97struct PayloadParserFn<F> {
98  parse: F,
99}
100
101impl<F> PayloadParser for PayloadParserFn<F>
102where
103  F: Fn(&str, &Args) -> Option<PayloadBox> + Send + Sync + 'static,
104{
105  fn parse(&self, command: RStr<'_>, args: &Args) -> ROption<PayloadBox> {
106    (self.parse)(&command, args).into()
107  }
108}
109
110pub fn payload_parser<T: Clone + Send + Sync + 'static>(
111  parse: impl Fn(&str, &Args) -> Option<T> + Send + Sync + 'static,
112) -> PayloadParserBox {
113  PayloadParserBox::from_value(
114    PayloadParserFn {
115      parse: move |command: &str, args: &Args| parse(command, args).map(PayloadBox::new),
116    },
117    TD_Opaque,
118  )
119}
120
121pub trait HostExt {
122  fn component<T: Component>(&self, name: &str);
123  fn renderable<T: Renderable>(&self, name: &str);
124  fn payload<T: Payload>(&self, command: &str);
125  fn desktop_item<T: DesktopItem>(&self, name: &str);
126  fn desktop_item_deployer<T: DeployableDesktopItem>(&self, name: &str);
127  fn compositor<T: Compositor>(&self, name: &str);
128  fn spotlight<T: Spotlight>(&self, name: &str);
129  fn quick_settings<T: QuickSettings>(&self, name: &str);
130  fn config_parser<T: Send + Sync + 'static>(
131    &self,
132    name: &str,
133    parse: impl Fn(Option<&kdl::KdlNode>) -> T + Send + Sync + 'static,
134  );
135  fn config_parser_try<T: Send + Sync + 'static>(
136    &self,
137    name: &str,
138    parse: impl Fn(Option<&kdl::KdlNode>) -> Result<T, String> + Send + Sync + 'static,
139  );
140  fn service<T: Service>(&self, name: &str);
141  fn service_autostart<T: Service>(&self, name: &str);
142  fn foreground_service<T: ForegroundService>(&self, name: &str);
143  fn payload_parser<T: Clone + Send + Sync + 'static>(
144    &self,
145    commands: &[&str],
146    parse: impl Fn(&str, &Args) -> Option<T> + Send + Sync + 'static,
147  );
148  fn command(&self, name: &str, title: &str);
149  fn command_with(&self, name: &str, title: &str, description: &str);
150  fn style(&self, css: &str);
151}
152
153impl HostExt for Host_TO<RBox<()>> {
154  fn component<T: Component>(&self, name: &str) {
155    Host::register_component(
156      self,
157      RString::from(name),
158      crate::surfaces::component_factory::<T>(),
159    );
160  }
161
162  fn renderable<T: Renderable>(&self, name: &str) {
163    Host::register_renderable(
164      self,
165      RString::from(name),
166      crate::surfaces::renderable_factory::<T>(),
167    );
168  }
169
170  fn payload<T: Payload>(&self, command: &str) {
171    Host::register_payload(
172      self,
173      RString::from(command),
174      crate::surfaces::payload_factory::<T>(),
175    );
176  }
177
178  fn desktop_item<T: DesktopItem>(&self, name: &str) {
179    Host::register_desktop_item(
180      self,
181      RString::from(name),
182      crate::surfaces::desktop_item_factory::<T>(),
183    );
184  }
185
186  fn desktop_item_deployer<T: DeployableDesktopItem>(&self, name: &str) {
187    Host::register_desktop_item_deployer(
188      self,
189      RString::from(name),
190      crate::surfaces::deployable_desktop_item_factory::<T>(),
191    );
192  }
193
194  fn compositor<T: Compositor>(&self, name: &str) {
195    Host::register_compositor(
196      self,
197      RString::from(name),
198      crate::surfaces::compositor_factory::<T>(),
199    );
200  }
201
202  fn spotlight<T: Spotlight>(&self, name: &str) {
203    Host::register_spotlight(
204      self,
205      RString::from(name),
206      crate::surfaces::spotlight_factory::<T>(),
207    );
208  }
209
210  fn quick_settings<T: QuickSettings>(&self, name: &str) {
211    Host::register_quick_settings(
212      self,
213      RString::from(name),
214      crate::surfaces::quick_settings_factory::<T>(),
215    );
216  }
217
218  fn config_parser<T: Send + Sync + 'static>(
219    &self,
220    name: &str,
221    parse: impl Fn(Option<&kdl::KdlNode>) -> T + Send + Sync + 'static,
222  ) {
223    Host::register_config_parser(self, RString::from(name), config_parser(name, parse));
224  }
225
226  fn config_parser_try<T: Send + Sync + 'static>(
227    &self,
228    name: &str,
229    parse: impl Fn(Option<&kdl::KdlNode>) -> Result<T, String> + Send + Sync + 'static,
230  ) {
231    Host::register_config_parser(self, RString::from(name), config_parser_try(name, parse));
232  }
233
234  fn service<T: Service>(&self, name: &str) {
235    Host::register_service(self, RString::from(name), service_factory::<T>(), false);
236  }
237
238  fn service_autostart<T: Service>(&self, name: &str) {
239    Host::register_service(self, RString::from(name), service_factory::<T>(), true);
240  }
241
242  fn foreground_service<T: ForegroundService>(&self, name: &str) {
243    Host::register_foreground_service(self, RString::from(name), foreground_service_factory::<T>());
244  }
245
246  fn payload_parser<T: Clone + Send + Sync + 'static>(
247    &self,
248    commands: &[&str],
249    parse: impl Fn(&str, &Args) -> Option<T> + Send + Sync + 'static,
250  ) {
251    let commands: RVec<RString> = commands
252      .iter()
253      .map(|command| RString::from(*command))
254      .collect();
255    Host::register_payload_parser(self, commands, payload_parser(parse));
256  }
257
258  fn command(&self, name: &str, title: &str) {
259    Host::register_command(
260      self,
261      RString::from(name),
262      RString::from(title),
263      RString::new(),
264    );
265  }
266
267  fn command_with(&self, name: &str, title: &str, description: &str) {
268    Host::register_command(
269      self,
270      RString::from(name),
271      RString::from(title),
272      RString::from(description),
273    );
274  }
275
276  fn style(&self, css: &str) {
277    Host::register_style(self, RStr::from_str(css));
278  }
279}