1use abi_stable::pointer_trait::ImmutableRef;
2use abi_stable::std_types::{RArc, ROption, RStr, RString};
3use abi_stable::{RRef, StableAbi, sabi_trait, traits::IntoReprC};
4use std::fmt::Debug;
5
6pub const MAX_SCORE: u32 = u32::MAX;
8
9pub const MIN_SCORE: u32 = u32::MIN;
11
12pub type ArcDynHit = Hit_TO<'static, RArc<()>>;
16
17#[sabi_trait]
24pub trait Hit: Sync + Send + Debug {
25 fn title(&self) -> RStr<'_>;
26
27 fn subtitle(&self) -> RStr<'_> {
28 RStr::default()
29 }
30
31 fn override_score(&self) -> ROption<u32> {
38 ROption::RNone
39 }
40
41 fn action(&self, context: RefDynHitActionContext<'_>);
42
43 fn secondary_action(&self, context: RefDynHitActionContext<'_>) {
48 log::trace!("no secondary action set for hit '{}'", self.title());
49
50 self.action(context);
51 }
52}
53
54pub fn clone_hit_arc(hit: &ArcDynHit) -> ArcDynHit {
58 ArcDynHit::from_sabi(hit.obj.shallow_clone())
59}
60
61#[repr(C)]
62#[derive(StableAbi, Debug, PartialEq, Eq, Copy, Clone)]
63pub enum ActionKind {
64 Primary,
65 Secondary,
66}
67
68#[repr(C)]
70#[derive(StableAbi, Debug)]
71pub struct ScoredHit {
72 pub hit: ArcDynHit,
73 pub score: u32,
74}
75
76impl ScoredHit {
77 pub fn from(hit: ArcDynHit, score: u32) -> Self {
78 Self { hit, score }
79 }
80}
81
82pub type RefDynHitActionContext<'a> = HitActionContext_TO<'static, RRef<'a, ()>>;
84
85#[sabi_trait]
87pub trait HitActionContext {
88 fn hide_frontend(&self);
90
91 fn refresh_frontend(&self);
95
96 fn exit(&self);
98
99 fn restart(&self);
101
102 fn set_query(&self, query: RString);
104
105 fn clear_caches(&self);
107
108 fn set_clipboard_text(&self, content: RString);
110}
111
112type SimpleHitAction = Box<dyn Fn(&SimpleHit, RefDynHitActionContext<'_>) + Send + Sync>;
113
114#[repr(C)]
116pub struct SimpleHit {
117 pub title: RString,
118 pub subtitle: RString,
119 pub override_score: ROption<u32>,
120 pub action: SimpleHitAction,
121 pub secondary_action: Option<SimpleHitAction>,
122}
123
124impl Debug for SimpleHit {
125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126 f.debug_struct("SimpleHit")
127 .field("title", &self.title)
128 .field("subtitle", &self.subtitle)
129 .field("override_score", &self.override_score)
130 .field("action", &format_args!("Fn@{:p}", self.action.to_raw_ptr()))
131 .finish()
132 }
133}
134
135impl SimpleHit {
136 #[must_use]
137 pub fn new(
138 title: impl Into<RString>,
139 subtitle: impl Into<RString>,
140 func: impl Fn(&Self, RefDynHitActionContext<'_>) + Send + Sync + 'static,
141 ) -> Self {
142 Self {
143 title: title.into(),
144 subtitle: subtitle.into(),
145 override_score: ROption::RNone,
146 action: Box::new(func),
147 secondary_action: None,
148 }
149 }
150
151 #[must_use]
155 pub fn with_score(mut self, score: impl Into<Option<u32>>) -> Self {
156 let option: Option<u32> = score.into();
157 self.override_score = option.into_c();
158 self
159 }
160
161 #[must_use]
165 pub fn with_secondary(
166 mut self,
167 action: impl Fn(&Self, RefDynHitActionContext<'_>) + Send + Sync + 'static,
168 ) -> Self {
169 self.secondary_action = Some(Box::new(action));
170 self
171 }
172}
173
174impl Hit for SimpleHit {
175 fn action(&self, context: RefDynHitActionContext<'_>) {
176 (self.action)(self, context);
177 }
178
179 fn secondary_action(&self, context: RefDynHitActionContext<'_>) {
180 let Some(action) = &self.secondary_action else {
181 log::trace!("no secondary action set for hit '{}'", self.title());
182
183 return self.action(context);
184 };
185
186 (action)(self, context);
187 }
188
189 fn title(&self) -> RStr<'_> {
190 self.title.as_rstr()
191 }
192
193 fn subtitle(&self) -> RStr<'_> {
194 self.subtitle.as_rstr()
195 }
196
197 fn override_score(&self) -> ROption<u32> {
198 self.override_score
199 }
200}
201
202impl From<SimpleHit> for ArcDynHit {
203 fn from(value: SimpleHit) -> Self {
204 Self::from_ptr(RArc::new(value), sabi_trait::TD_Opaque)
205 }
206}