matchmaker/nucleo/
injector.rs

1// Original code from https://github.com/helix-editor/helix (MPL 2.0)
2// Modified by Squirreljetpack, 2025
3
4use std::{
5    fmt::{self, Display, Formatter},
6    marker::PhantomData,
7    sync::{
8        Arc,
9        atomic::{AtomicU32, Ordering},
10    },
11};
12
13use super::worker::{Column, Worker, WorkerError};
14use crate::{PickerItem, SegmentableItem, nucleo::variants::ColumnIndexable};
15
16pub trait Injector: Clone {
17    type InputItem;
18    type Inner: Injector;
19    type Context;
20
21    fn new(injector: Self::Inner, data: Self::Context) -> Self;
22    fn inner(&self) -> &Self::Inner;
23    fn wrap(
24        &self,
25        item: Self::InputItem,
26    ) -> Result<<Self::Inner as Injector>::InputItem, WorkerError>;
27
28    fn push(&self, item: Self::InputItem) -> Result<(), WorkerError> {
29        let item = self.wrap(item)?;
30        self.inner().push(item)
31    }
32}
33
34impl Injector for () {
35    fn inner(&self) -> &Self::Inner {
36        unreachable!()
37    }
38    fn new(_: Self::Inner, _: Self::Context) -> Self {
39        unreachable!()
40    }
41    fn wrap(
42        &self,
43        _: Self::InputItem,
44    ) -> Result<<Self::Inner as Injector>::InputItem, WorkerError> {
45        unreachable!()
46    }
47
48    type Context = ();
49    type Inner = ();
50    type InputItem = ();
51}
52
53pub struct WorkerInjector<T, C = ()> {
54    pub(super) inner: nucleo::Injector<T>,
55    pub(super) columns: Arc<[Column<T, C>]>,
56    pub(super) context: Arc<C>,
57    pub(super) version: u32,
58    pub(super) picker_version: Arc<AtomicU32>,
59}
60
61
62
63impl<T: PickerItem, C> Injector for WorkerInjector<T, C> {
64    type InputItem = T;
65    type Inner = ();
66    type Context = Worker<T, C>;
67
68    fn new(_: Self::Inner, data: Self::Context) -> Self {
69        data.injector()
70    }
71
72    fn inner(&self) -> &Self::Inner {
73        &()
74    }
75
76    fn wrap(
77        &self,
78        _: Self::InputItem,
79    ) -> Result<<Self::Inner as Injector>::InputItem, WorkerError> {
80        Ok(())
81    }
82
83    fn push(&self, item: T) -> Result<(), WorkerError> {
84        if self.version != self.picker_version.load(Ordering::Relaxed) {
85            return Err(WorkerError::InjectorShutdown);
86        }
87        push_impl(&self.inner, &self.columns, item, &self.context);
88        Ok(())
89    }
90}
91
92pub(super) fn push_impl<T, C>(injector: &nucleo::Injector<T>, columns: &[Column<T, C>], item: T, context: &C) {
93    injector.push(item, |item, dst| {
94        for (column, text) in columns.iter().filter(|column| column.filter).zip(dst) {
95            *text = column.format_text(item, context).into()
96        }
97    });
98}
99
100// ------------- Wrapper structs
101#[derive(Debug, Clone, Hash, Eq, PartialEq)]
102pub struct Segmented<T: SegmentableItem> {
103    pub inner: T,
104    ranges: Arc<[(usize, usize)]>,
105}
106
107impl<T: SegmentableItem> ColumnIndexable for Segmented<T> {
108    fn index(&self, index: usize) -> &str {
109        if let Some((start, end)) = self.ranges.get(index) {
110            &self.inner[*start..*end]
111        } else {
112            ""
113        }
114    }
115}
116
117impl<T: Display + SegmentableItem> Display for Segmented<T> {
118    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
119        write!(f, "{}", self.inner)
120    }
121}
122
123impl<T: Display> Display for Indexed<T> {
124    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
125        write!(f, "{}", self.inner)
126    }
127}
128
129#[derive(Debug, Clone, Hash, Eq, PartialEq)]
130pub struct Indexed<T> {
131    pub index: u32,
132    pub inner: T,
133}
134
135impl<T: Clone> Indexed<T> {
136    pub fn identifier(&self) -> (u32, T) {
137        (self.index, self.inner.clone())
138    }
139}
140
141impl<T: ColumnIndexable> ColumnIndexable for Indexed<T> {
142    fn index(&self, index: usize) -> &str {
143        self.inner.index(index)
144    }
145}
146
147// ----- Injectors
148
149pub struct IndexedInjector<T, I: Injector<InputItem = Indexed<T>>> {
150    injector: I,
151    count: Arc<AtomicU32>,
152    input_type: PhantomData<T>,
153}
154
155impl<T, I: Injector<InputItem = Indexed<T>>> Injector for IndexedInjector<T, I> {
156    type InputItem = T;
157    type Inner = I;
158    type Context = ();
159
160    fn new(injector: Self::Inner, _data: Self::Context) -> Self {
161        Self {
162            injector,
163            count: Arc::new(AtomicU32::new(0)),
164            input_type: PhantomData,
165        }
166    }
167
168    fn wrap(
169        &self,
170        item: Self::InputItem,
171    ) -> Result<<Self::Inner as Injector>::InputItem, WorkerError> {
172        let index = self.count.fetch_add(1, Ordering::Relaxed);
173        Ok(Indexed { index, inner: item })
174    }
175
176    fn inner(&self) -> &Self::Inner {
177        &self.injector
178    }
179}
180
181pub struct SegmentedInjector<T: SegmentableItem, I: Injector<InputItem = Segmented<T>>> {
182    injector: I,
183    splitter: Arc<dyn Fn(&T) -> Vec<(usize, usize)> + Send + Sync + 'static>,
184    input_type: PhantomData<T>,
185}
186
187impl<T: SegmentableItem, I: Injector<InputItem = Segmented<T>>> Injector
188    for SegmentedInjector<T, I>
189{
190    type InputItem = T;
191    type Inner = I;
192    type Context = Arc<dyn Fn(&T) -> Vec<(usize, usize)> + Send + Sync + 'static>;
193
194    fn new(injector: Self::Inner, data: Self::Context) -> Self {
195        Self {
196            injector,
197            splitter: data,
198            input_type: PhantomData,
199        }
200    }
201
202    fn wrap(
203        &self,
204        item: Self::InputItem,
205    ) -> Result<<Self::Inner as Injector>::InputItem, WorkerError> {
206        let ranges = Arc::from((self.splitter)(&item).into_boxed_slice());
207        Ok(Segmented {
208            inner: item,
209            ranges,
210        })
211    }
212
213    fn inner(&self) -> &Self::Inner {
214        &self.injector
215    }
216}
217
218
219// ----------- CLONE ----------------------------
220impl<T, C> Clone for WorkerInjector<T, C> {
221    fn clone(&self) -> Self {
222        Self {
223            inner: self.inner.clone(),
224            columns: Arc::clone(&self.columns),
225            context: Arc::clone(&self.context),
226            version: self.version,
227            picker_version: Arc::clone(&self.picker_version),
228        }
229    }
230}
231
232impl<T, I: Injector<InputItem = Indexed<T>>> Clone for IndexedInjector<T, I> {
233    fn clone(&self) -> Self {
234        Self {
235            injector: self.injector.clone(),
236            count: Arc::clone(&self.count),
237            input_type: PhantomData,
238        }
239    }
240}
241
242impl<T: SegmentableItem, I: Injector<InputItem = Segmented<T>>> Clone for SegmentedInjector<T, I> {
243    fn clone(&self) -> Self {
244        Self {
245            injector: self.injector.clone(),
246            splitter: Arc::clone(&self.splitter),
247            input_type: PhantomData,
248        }
249    }
250}