gravel_ffi/provider.rs
1use crate::config::PluginConfigAdapter;
2use crate::hit::{ArcDynHit, clone_hit_arc};
3use abi_stable::StableAbi;
4use abi_stable::sabi_trait;
5use abi_stable::std_types::{RBox, RStr, RVec};
6
7/// FFI-safe [`ProviderInner`] trait object.
8pub type BoxDynProvider = ProviderInner_TO<'static, RBox<()>>;
9
10/// This trait is auto-implemented with the [`crate::gravel_provider`] macro.
11///
12/// It does some boilerplate conversions to reduce complexity in the real [`Provider`].
13#[sabi_trait]
14pub trait ProviderInner: Send {
15 fn query(&self, query: RStr<'_>) -> ProviderResult;
16 fn clear_caches(&self) {
17 // do nothing
18 }
19}
20
21/// Abstracts functionality required for a provider.
22///
23/// Implement this and add the [`crate::gravel_provider`] macro to write a provider plugin:
24/// ```
25/// use gravel_ffi::prelude::*;
26///
27/// pub struct MyProvider {
28/// // put state here
29/// }
30///
31/// // give the plugin a memorable name
32/// #[gravel_provider("my_provider")]
33/// impl Provider for MyProvider {
34/// fn new(config: &PluginConfigAdapter<'_>) -> Self {
35/// Self { }
36/// }
37///
38/// fn query(&self, query: &str) -> ProviderResult {
39/// ProviderResult::empty()
40/// }
41/// }
42/// ```
43pub trait Provider {
44 /// Constructs a new provider.
45 fn new(config: &PluginConfigAdapter<'_>) -> Self;
46
47 /// Queries the provider and returns hits.
48 fn query(&self, query: &str) -> ProviderResult;
49
50 /// Clears all caches the provider may keep.
51 fn clear_caches(&self) {
52 // do nothing
53 }
54}
55
56/// A collection of hits returned by a provider.
57#[repr(C)]
58#[derive(StableAbi, Debug)]
59pub struct ProviderResult {
60 pub hits: RVec<ArcDynHit>,
61}
62
63impl ProviderResult {
64 /// Constructs a new [`ProviderResult`] from some hits.
65 #[must_use]
66 pub fn new(hits: impl IntoIterator<Item = impl Into<ArcDynHit>>) -> Self {
67 let hits = hits.into_iter().map(Into::into).collect();
68 Self { hits }
69 }
70
71 /// Constructs a new [`ProviderResult`] from some borrowed hits, cloning them (incrementing the Rc).
72 #[must_use]
73 pub fn from_cached<'a>(hits: impl IntoIterator<Item = &'a ArcDynHit>) -> Self {
74 let hits = hits.into_iter().map(clone_hit_arc).collect();
75 Self { hits }
76 }
77
78 /// Constructs a new [`ProviderResult`] with one or no hits.
79 #[must_use]
80 pub fn from_option(hit: Option<impl Into<ArcDynHit>>) -> Self {
81 hit.map_or_else(Self::empty, Self::single)
82 }
83
84 /// Constructs an empty [`ProviderResult`].
85 #[must_use]
86 pub fn empty() -> Self {
87 Self { hits: RVec::new() }
88 }
89
90 /// Constructs a new [`ProviderResult`] from a single hit.
91 #[must_use]
92 pub fn single(hit: impl Into<ArcDynHit>) -> Self {
93 let hits = vec![hit.into()].into();
94 Self { hits }
95 }
96}