covey_plugin/lib.rs
1pub mod manifest;
2pub mod rank;
3
4mod list;
5use std::{
6 path::PathBuf,
7 sync::{LazyLock, OnceLock},
8};
9
10pub use list::{Icon, List, ListItem, ListStyle};
11mod action;
12pub use action::{Action, Actions};
13mod input;
14pub use input::{Input, SelectionRange};
15mod plugin;
16pub use plugin::Plugin;
17mod server;
18pub use server::run_server;
19mod menu;
20pub use menu::Menu;
21pub mod spawn;
22mod store;
23
24pub use anyhow::{self, Result};
25
26/// ID of this plugin.
27///
28/// This should be set to `env!("CARGO_PKG_NAME")` of the plugin, by
29/// inputting string to [`main`].
30pub static PLUGIN_ID: OnceLock<&'static str> = OnceLock::new();
31
32/// Assigned directory of this plugin, where extra data can be stored.
33///
34/// This is <data-dir>/covey/plugins/<plugin-id>/. The directory should already
35/// contain this plugin's binary (with the name of <plugin-id>) and
36/// a `manifest.toml`.
37///
38/// [`covey_plugin`](crate) will also add an `activations.json` file. See
39/// the [`rank`] module for more details.
40///
41/// This depends on the [`PLUGIN_ID`] static being set before first being called.
42pub fn plugin_data_dir() -> &'static PathBuf {
43 static DIR: LazyLock<PathBuf> = LazyLock::new(|| {
44 dirs::data_dir()
45 .expect("data dir should exist")
46 .join("covey")
47 .join("plugins")
48 .join(
49 PLUGIN_ID
50 .get()
51 .expect("plugin id should be initialised in main"),
52 )
53 });
54 &*DIR
55}
56
57/// Clones variables into an async closure (by calling [`ToOwned::to_owned`]).
58///
59/// The closure will automatically be turned into a
60/// `move || async move {...}` closure. Closure arguments are supported.
61///
62/// All variables will be cloned immediately for the closure to own
63/// the variable. The variable will also be cloned every time the
64/// closure is called, so that the async body can also own the variables.
65///
66/// # Examples
67/// Expansion:
68/// ```
69/// # use covey_plugin::clone_async;
70/// let thing = String::from("important info");
71/// let foo = String::from("bar");
72/// clone_async!(thing, foo, || {
73/// println!("some {thing} from {foo}");
74/// });
75/// // Expands to:
76/// // let thing = thing.to_owned();
77/// // let foo = foo.to_owned();
78/// // move || {
79/// // let thing = thing.to_owned();
80/// // let foo = foo.to_owned();
81/// // async move {
82/// // println!("some {thing} from {foo}");
83/// // }
84/// // }
85/// ```
86///
87/// This will most often be used in the context of adding callbacks to
88/// list items.
89/// ```ignore
90/// # use covey_plugin::{clone_async, ListItem};
91/// let thing = String::from("important info");
92/// let foo = String::from("bar");
93/// ListItem::new("title")
94/// .on_activate(clone_async!(thing, || {
95/// println!("some {thing}");
96/// todo!()
97/// }))
98/// .on_hotkey_activate(clone_async!(foo, |hotkey| {
99/// println!("foo {foo}! got hotkey {hotkey:?}");
100/// todo!()
101/// }));
102/// ```
103///
104/// If you have a more complex expression that you want to clone, you can
105/// bind it to an identifier using `ident = expr` syntax.
106/// ```ignore
107/// # use covey_plugin::{clone_async, ListItem};
108/// let thing = String::from("important info");
109/// let item = ListItem::new("get me out of here");
110/// ListItem::new("i got u")
111/// .on_activate(clone_async!(thing, title = item.title, || {
112/// println!("got {title} out of there with {thing}");
113/// todo!()
114/// }));
115/// ```
116#[macro_export]
117macro_rules! clone_async {
118 // this isn't correctly matched with the below so need a special case
119 // for empty closure args
120
121 (
122 $( $ident:ident $(= $expr:expr)? , )*
123 || $($tt:tt)*
124 ) => {
125 {
126 $(let $ident = ($crate::__clone_helper_choose_first!($($expr,)? $ident)).to_owned();)*
127 async move || {
128 // TODO: this extra clone isn't necessary.
129 // with an async closure, the future can *borrow* from variables
130 // captured (and owned) by the closure. however, this isn't clear
131 // from the types and the compiler errors are sub-par right now.
132 // this macro name also suggests that all variables should be owned.
133 // This will always clone the captured variables every time this
134 // closure is called, which is simpler to reason about and
135 // this has little impact to performance anyways.
136 $(let $ident = $ident.to_owned();)*
137 { $($tt)* }
138 }
139 }
140 };
141
142 (
143 $( $ident:ident $(= $expr:expr)? , )*
144 | $($args:pat_param),* $(,)? | $($tt:tt)*
145 ) => {
146 {
147 $(let $ident = ($crate::__clone_helper_choose_first!($($expr,)? $ident)).to_owned();)*
148 async move | $($args),* | {
149 $(let $ident = $ident.to_owned();)*
150 { $($tt)* }
151 }
152 }
153 };
154
155}
156
157/// Private implementation detail of [`clone_async!`].
158#[doc(hidden)]
159#[macro_export]
160macro_rules! __clone_helper_choose_first {
161 ($a:expr) => {
162 $a
163 };
164 ($a:expr, $b: expr) => {
165 $a
166 };
167}