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    marker::PhantomData,
6    sync::{
7        Arc,
8        atomic::{AtomicU32, Ordering},
9    },
10};
11
12use super::worker::{Column, Worker, WorkerError};
13use super::{Indexed, Segmented};
14use crate::{MMItem, SegmentableItem, SplitterFn};
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: MMItem, 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// ----- Injectors
101
102pub struct IndexedInjector<T, I: Injector<InputItem = Indexed<T>>> {
103    injector: I,
104    count: Arc<AtomicU32>,
105    input_type: PhantomData<T>,
106}
107
108impl<T, I: Injector<InputItem = Indexed<T>>> Injector for IndexedInjector<T, I> {
109    type InputItem = T;
110    type Inner = I;
111    type Context = ();
112
113    fn new(injector: Self::Inner, _data: Self::Context) -> Self {
114        Self {
115            injector,
116            count: Arc::new(AtomicU32::new(0)),
117            input_type: PhantomData,
118        }
119    }
120
121    fn wrap(
122        &self,
123        item: Self::InputItem,
124    ) -> Result<<Self::Inner as Injector>::InputItem, WorkerError> {
125        let index = self.count.fetch_add(1, Ordering::Relaxed);
126        Ok(Indexed { index, inner: item })
127    }
128
129    fn inner(&self) -> &Self::Inner {
130        &self.injector
131    }
132}
133
134
135pub struct SegmentedInjector<T: SegmentableItem, I: Injector<InputItem = Segmented<T>>> {
136    injector: I,
137    splitter: SplitterFn<T>,
138    input_type: PhantomData<T>,
139}
140
141impl<T: SegmentableItem, I: Injector<InputItem = Segmented<T>>> Injector
142    for SegmentedInjector<T, I>
143{
144    type InputItem = T;
145    type Inner = I;
146    type Context = SplitterFn<T>;
147
148    fn new(injector: Self::Inner, data: Self::Context) -> Self {
149        Self {
150            injector,
151            splitter: data,
152            input_type: PhantomData,
153        }
154    }
155
156    fn wrap(
157        &self,
158        item: Self::InputItem,
159    ) -> Result<<Self::Inner as Injector>::InputItem, WorkerError> {
160        let ranges = Arc::from((self.splitter)(&item).into_boxed_slice());
161        Ok(Segmented {
162            inner: item,
163            ranges,
164        })
165    }
166
167    fn inner(&self) -> &Self::Inner {
168        &self.injector
169    }
170
171    fn push(&self, item: Self::InputItem) -> Result<(), WorkerError> {
172        let item = self.wrap(item)?;
173        self.inner().push(item)
174    }
175}
176
177
178// ----------- CLONE ----------------------------
179impl<T, C> Clone for WorkerInjector<T, C> {
180    fn clone(&self) -> Self {
181        Self {
182            inner: self.inner.clone(),
183            columns: Arc::clone(&self.columns),
184            context: Arc::clone(&self.context),
185            version: self.version,
186            picker_version: Arc::clone(&self.picker_version),
187        }
188    }
189}
190
191impl<T, I: Injector<InputItem = Indexed<T>>> Clone for IndexedInjector<T, I> {
192    fn clone(&self) -> Self {
193        Self {
194            injector: self.injector.clone(),
195            count: Arc::clone(&self.count),
196            input_type: PhantomData,
197        }
198    }
199}
200
201impl<T: SegmentableItem, I: Injector<InputItem = Segmented<T>>> Clone for SegmentedInjector<T, I> {
202    fn clone(&self) -> Self {
203        Self {
204            injector: self.injector.clone(),
205            splitter: Arc::clone(&self.splitter),
206            input_type: PhantomData,
207        }
208    }
209}