matchmaker/nucleo/
variants.rs

1use std::{borrow::Cow, sync::Arc};
2
3use crate::{
4    PickerItem,
5};
6
7use super::{injector::{self, Indexed}, Text, worker::{Column, Worker}};
8
9// C is not generic because not sure about how C should be used/passed
10impl<T: PickerItem> Worker<T> {
11    /// Returns a function which templates a string given an item using the column functions
12    pub fn make_format_fn<const QUOTE: bool>(
13        &self,
14        blank_format: impl Fn(&T) -> &str + Send + Sync + 'static,
15    ) -> Box<dyn Fn(&T, &str) -> String + Send + Sync> {
16        let columns = self.columns.clone();
17        
18        Box::new(move |item: &T, template: &str| {
19            let mut result = String::with_capacity(template.len());
20            let mut chars = template.chars().peekable();
21            let mut state = State::Normal;
22            let mut key = String::new();
23            
24            enum State {
25                Normal,
26                InKey,
27                Escape,
28            }
29            
30            while let Some(c) = chars.next() {
31                match state {
32                    State::Normal => match c {
33                        '\\' => state = State::Escape,
34                        '{' => state = State::InKey,
35                        _ => result.push(c),
36                    },
37                    State::Escape => {
38                        result.push(c);
39                        state = State::Normal;
40                    }
41                    State::InKey => match c {
42                        '}' => {
43                            let replacement = match key.as_str() {
44                                "" => Cow::Borrowed(blank_format(item)),
45                                _ => columns
46                                .iter()
47                                .find(|col| &*col.name == key.as_str())
48                                .map(|col| col.format_text(item, &()))
49                                .unwrap_or_else(|| Cow::Borrowed("")),
50                            };
51                            
52                            if QUOTE {
53                                result.push('\'');
54                                result.push_str(&replacement);
55                                result.push('\'');
56                            } else {
57                                result.push_str(&replacement);
58                            }
59                            key.clear();
60                            state = State::Normal;
61                        }
62                        _ => key.push(c),
63                    },
64                }
65            }
66            
67            if !key.is_empty() {
68                result.push('{');
69                result.push_str(&key);
70            }
71            
72            result
73        })
74    }
75}
76
77pub trait Render {
78    fn as_str(&self) -> &str;
79}
80impl<T: AsRef<str>> Render for T {
81    fn as_str(&self) -> &str {
82        self.as_ref()
83    }
84}
85
86impl<T: Render + PickerItem> Worker<Indexed<T>> {
87    pub fn new_single() -> Self {
88        Self::new(
89            vec![Column::new("_", |item: &Indexed<T>, _context: &()| {
90                Text::from(item.inner.as_str())
91            })],
92            0,
93            (),
94        )
95    }
96    
97    /// A convenience method to initialize data. Note that it is clearly unsound to use this concurrently with other workers, or to subsequently push with an IndexedInjector.
98    pub fn append(&self, items: impl IntoIterator<Item = T>) -> u32 {
99        let mut index = self.nucleo.snapshot().item_count();
100        for inner in items {
101            injector::push_impl(
102                &self.nucleo.injector(),
103                &self.columns,
104                Indexed { index, inner },
105                &(),
106            );
107            index += 1;
108        }
109        index
110    }
111
112    pub fn clone_identifier(item: &Indexed<T>) -> (u32, T) 
113    where T: Clone {
114        (item.index, item.inner.clone())
115    }
116}
117
118pub trait ColumnIndexable {
119    fn index(&self, i: usize) -> &str;
120}
121
122impl<T> Worker<T>
123where
124T: ColumnIndexable + PickerItem,
125{
126    pub fn new_indexable<I, S>(column_names: I) -> Self
127    where
128    I: IntoIterator<Item = S>,
129    S: Into<Arc<str>>,
130    {
131        let columns = column_names.into_iter().enumerate().map(|(i, name)| {
132            let name = name.into();
133            
134            Column::new(name, move |item: &T, _| {
135                let text = item.index(i);
136                Text::from(text)
137            })
138        });
139        
140        Self::new(columns, 0, ())
141    }
142}