gravel_core/
lib.rs

1//! gravel's core library.
2//!
3//! Contains functionality used internally by gravel, and is generally not
4//! required for writing plugins.
5
6use abi_stable::external_types::crossbeam_channel::{RReceiver, RSender};
7use abi_stable::sabi_trait;
8use abi_stable::std_types::{ROption, RString};
9use abi_stable::traits::{IntoReprC, IntoReprRust};
10use engine::QueryEngine;
11use gravel_ffi::{ActionKind, ArcDynHit, clone_hit_arc};
12use gravel_ffi::{BoxDynFrontendContext, FrontendContext, FrontendMessage, FrontendMessageNe};
13use std::sync::atomic::{AtomicU32, Ordering};
14use std::{thread, time::Duration};
15
16use crate::clipboard::Clipboard;
17
18pub mod clipboard;
19pub mod config;
20pub mod engine;
21pub mod hotkeys;
22pub mod paths;
23pub mod performance;
24pub mod plugin;
25pub mod scoring;
26
27pub struct Core {
28	engine: QueryEngine,
29	frontend_sender: RSender<FrontendMessageNe>,
30	receiver: RReceiver<CoreMessage>,
31	clipboard: Clipboard,
32}
33
34pub enum CoreMessage {
35	Frontend(FrontendMessage),
36	Query(u32, String),
37	RunAction(ArcDynHit, ActionKind),
38	ClearCaches,
39	SetClipboardText(String),
40}
41
42impl Core {
43	pub fn new(
44		engine: QueryEngine,
45		frontend_sender: RSender<FrontendMessageNe>,
46		receiver: RReceiver<CoreMessage>,
47	) -> Self {
48		Self {
49			engine,
50			frontend_sender,
51			receiver,
52			clipboard: Clipboard::new(),
53		}
54	}
55
56	pub fn spawn(self) {
57		thread::spawn(move || self.run());
58	}
59
60	pub fn run(&self) {
61		loop {
62			self.receive_message();
63		}
64	}
65
66	fn receive_message(&self) -> Option<()> {
67		const ONE_MILLI: Duration = Duration::from_millis(1);
68
69		match self.receiver.recv_timeout(ONE_MILLI).ok()? {
70			CoreMessage::Frontend(msg) => self.send_frontend(msg),
71			CoreMessage::Query(token, query) => self.query(token, &query),
72			CoreMessage::RunAction(hit, kind) => self.run_action(&hit, kind),
73			CoreMessage::ClearCaches => self.clear_caches(),
74			CoreMessage::SetClipboardText(content) => self.clipboard.set_text(&content),
75		}
76
77		None
78	}
79
80	fn run_action(&self, hit: &ArcDynHit, kind: ActionKind) {
81		timed!("hit action took", {
82			self.engine.run_hit_action(hit, kind);
83		});
84	}
85
86	fn query(&self, token: u32, query: &str) {
87		let result = timed!(("full query {token} took"), { self.engine.query(query.into_c()) });
88
89		self.send_frontend(FrontendMessage::QueryResult(token, result));
90	}
91
92	fn clear_caches(&self) {
93		log::debug!("clearing caches");
94
95		self.send_frontend(FrontendMessage::ClearCaches);
96		self.engine.clear_caches();
97	}
98
99	fn send_frontend(&self, message: FrontendMessage) {
100		self.frontend_sender
101			.send(FrontendMessageNe::new(message))
102			.inspect_err(|e| log::error!("unable to send frontend message: {e}"))
103			.ok();
104	}
105}
106
107pub struct FrontendCtx {
108	receiver: RReceiver<FrontendMessageNe>,
109	sender: RSender<CoreMessage>,
110	token_counter: AtomicU32,
111}
112
113impl FrontendCtx {
114	pub fn new(receiver: RReceiver<FrontendMessageNe>, sender: RSender<CoreMessage>) -> Self {
115		Self {
116			receiver,
117			sender,
118			token_counter: Default::default(),
119		}
120	}
121
122	fn send(&self, message: CoreMessage) {
123		self.sender
124			.send(message)
125			.inspect_err(|e| log::error!("unable to send core message: {e}"))
126			.ok();
127	}
128
129	fn new_token(&self) -> u32 {
130		self.token_counter.fetch_add(1, Ordering::Relaxed)
131	}
132}
133
134impl FrontendContext for FrontendCtx {
135	fn recv_raw(&self) -> ROption<FrontendMessageNe> {
136		self.receiver.try_recv().ok().into_c()
137	}
138
139	fn query(&self, query: RString) -> u32 {
140		let token = self.new_token();
141		self.send(CoreMessage::Query(token, query.into_rust()));
142		token
143	}
144
145	fn run_hit_action(&self, hit: &ArcDynHit, kind: ActionKind) {
146		self.send(CoreMessage::RunAction(clone_hit_arc(hit), kind));
147	}
148}
149
150impl From<FrontendCtx> for BoxDynFrontendContext {
151	fn from(value: FrontendCtx) -> Self {
152		Self::from_value(value, sabi_trait::TD_Opaque)
153	}
154}