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}