gravel_ffi/
frontend.rs

1use crate::{ActionKind, ArcDynHit, PluginConfigAdapter, ScoredHit};
2use abi_stable::std_types::{RBox, ROption, RString, RVec};
3use abi_stable::traits::IntoReprRust;
4use abi_stable::{StableAbi, sabi_trait};
5
6/// FFI-safe [`FrontendInner`] trait object.
7pub type BoxDynFrontend = FrontendInner_TO<'static, RBox<()>>;
8
9/// This trait is auto-implemented with the [`crate::gravel_frontend`] macro.
10///
11/// It does some boilerplate conversions to reduce complexity in the real [`Frontend`].
12#[sabi_trait]
13pub trait FrontendInner {
14	fn run(&mut self) -> FrontendExitStatusNe;
15}
16
17/// Abstracts functionality required for a frontend.
18///
19/// Implement this and add the [`crate::gravel_frontend`] macro to write a frontend plugin:
20/// ```
21/// use gravel_ffi::prelude::*;
22///
23/// pub struct MyFrontend {
24///     // put state here
25/// }
26///
27/// // give the plugin a memorable name
28/// #[gravel_frontend("my_frontend")]
29/// impl Frontend for MyFrontend {
30///     fn new(context: BoxDynFrontendContext, config: &PluginConfigAdapter<'_>) -> Self {
31///         // initialize UI here
32///         Self { }
33///     }
34///
35///     fn run(&mut self) -> FrontendExitStatus {
36///         // run UI here
37///
38///         // gravel will exit when this function returns
39///         FrontendExitStatus::Exit
40///     }
41/// }
42/// ```
43pub trait Frontend {
44	/// Constructs a new frontend. Events must be received and handled by calling [`FrontendContextExt::recv`].
45	fn new(engine: BoxDynFrontendContext, config: &PluginConfigAdapter<'_>) -> Self;
46
47	/// Runs the UI; must block until ready to exit.
48	fn run(&mut self) -> FrontendExitStatus;
49}
50
51/// FFI-safe [`FrontendContext`] trait object.
52pub type BoxDynFrontendContext = FrontendContext_TO<'static, RBox<()>>;
53
54/// Context object providing core functionality to the [`Frontend`].
55#[sabi_trait]
56pub trait FrontendContext {
57	/// Use [`FrontendContextExt::recv`] instead.
58	#[doc(hidden)]
59	fn recv_raw(&self) -> ROption<FrontendMessageNe>;
60
61	/// Runs the query against configured providers and returns results.
62	///
63	/// The return value is a token unique to each query, allowing frontends to
64	/// ignore out-of-date results in lieu of cancellations.
65	fn query(&self, query: RString) -> u32;
66
67	/// Executes the passed hit's action.
68	fn run_hit_action(&self, hit: &ArcDynHit, kind: ActionKind);
69}
70
71pub trait FrontendContextExt: FrontendContext {
72	/// Attempts to receive one [`FrontendMessage`].
73	///
74	/// Returns [`None`] if there is no message or the message is from a newer version of the plugin interface.
75	fn recv(&self) -> Option<FrontendMessage> {
76		self.recv_raw()
77			.into_rust()?
78			.into_enum()
79			.inspect_err(|e| log::warn!("unknown FrontendMessage, this plugin is out of date: {e}"))
80			.ok()
81	}
82}
83
84impl<T: FrontendContext> FrontendContextExt for T {}
85
86/// A Collection of scored hits returned by the [`FrontendContext`].
87#[repr(C)]
88#[derive(StableAbi, Default, Debug)]
89pub struct QueryResult {
90	pub hits: RVec<ScoredHit>,
91}
92
93impl QueryResult {
94	pub fn new(hits: impl Into<RVec<ScoredHit>>) -> Self {
95		Self { hits: hits.into() }
96	}
97}
98
99/// Non-exhaustive wrapper around [`FrontendMessage`].
100pub type FrontendMessageNe = FrontendMessage_NE;
101
102/// Represents actions the [`Frontend`] should take.
103///
104/// These values are to be received by the frontend via
105/// [`FrontendContextExt::recv`] and must be handled.
106#[repr(u8)]
107#[derive(StableAbi, Debug)]
108#[sabi(kind(WithNonExhaustive(size = 40, traits(Debug))))]
109pub enum FrontendMessage {
110	ShowOrHide,
111	Show,
112	Hide,
113
114	/// Show the window and pre-populate the query with the parameter.
115	ShowWithQuery(RString),
116
117	/// Run the same query again.
118	Refresh,
119
120	/// Exit the appplication with [`FrontendExitStatus::Exit`].
121	Exit,
122
123	/// Exit the appplication with [`FrontendExitStatus::Restart`].
124	Restart,
125
126	/// Clear all caches the frontend may keep.
127	ClearCaches,
128
129	/// Result from a previous query.
130	///
131	/// The first value is a token unique to each query, allowing frontends to
132	/// ignore out-of-date results in lieu of cancellations.
133	QueryResult(u32, QueryResult),
134}
135
136/// Non-exhaustive wrapper around [`FrontendExitStatus`].
137pub type FrontendExitStatusNe = FrontendExitStatus_NE;
138
139/// This is returned when a [`Frontend`] exits and tells gravel what to do.
140#[repr(u8)]
141#[derive(StableAbi, Debug, Copy, Clone)]
142#[sabi(kind(WithNonExhaustive(size = 40, traits(Debug, Clone))))]
143pub enum FrontendExitStatus {
144	Exit,
145	Restart,
146}