matchmaker/nucleo/
variants.rs1use std::{borrow::Cow, sync::Arc};
2
3use crate::{RenderFn, SSS, nucleo::Indexed, utils::text::plain_text};
4
5use super::{
6 Text,
7 injector::{self},
8 worker::{Column, Worker},
9};
10
11impl<T: SSS> Worker<T> {
12 pub fn make_format_fn<const QUOTE: bool>(
14 &self,
15 blank_format: impl Fn(&T) -> Cow<'_, str> + SSS,
16 ) -> RenderFn<T> {
17 let columns = self.columns.clone();
18
19 Box::new(move |item: &T, template: &str| {
20 let mut result = String::with_capacity(template.len());
21 let chars = template.chars().peekable();
22 let mut state = State::Normal;
23 let mut key = String::new();
24
25 enum State {
26 Normal,
27 InKey,
28 Escape,
29 }
30
31 for c in chars {
32 match state {
33 State::Normal => match c {
34 '\\' => state = State::Escape,
35 '{' => state = State::InKey,
36 _ => result.push(c),
37 },
38 State::Escape => {
39 result.push(c);
40 state = State::Normal;
41 }
42 State::InKey => match c {
43 '}' => {
44 let replacement = match key.as_str() {
45 "" => blank_format(item),
46 _ => columns
47 .iter()
48 .find(|col| &*col.name == key.as_str())
49 .map(|col| col.format_text(item))
50 .unwrap_or_else(|| Cow::Borrowed("")),
51 };
52
53 if QUOTE {
54 result.push('\'');
55 result.push_str(&replacement);
56 result.push('\'');
57 } else {
58 result.push_str(&replacement);
59 }
60 key.clear();
61 state = State::Normal;
62 }
63 _ => key.push(c),
64 },
65 }
66 }
67
68 if !key.is_empty() {
69 result.push('{');
70 result.push_str(&key);
71 }
72
73 result
74 })
75 }
76}
77
78pub trait Render {
80 fn as_str(&self) -> Cow<'_, str> {
81 plain_text(&self.as_text()).into()
82 }
83 fn as_text(&self) -> Text<'_> {
84 Text::from(self.as_str())
85 }
86}
87impl<T: AsRef<str>> Render for T {
88 fn as_str(&self) -> Cow<'_, str> {
89 self.as_ref().into()
90 }
91}
92
93impl<T: Render + SSS> Worker<Indexed<T>> {
94 pub fn new_single_column() -> Self {
96 Self::new(
97 [Column::new("_", |item: &Indexed<T>| item.inner.as_text())],
98 0,
99 )
100 }
101
102 pub fn append(&self, items: impl IntoIterator<Item = T>) -> u32 {
104 let mut index = self.nucleo.snapshot().item_count();
105 for inner in items {
106 injector::push_impl(
107 &self.nucleo.injector(),
108 &self.columns,
109 Indexed { index, inner },
110 );
111 index += 1;
112 }
113 index
114 }
115}
116
117pub trait ColumnIndexable {
118 fn index(&self, i: usize) -> &str;
119}
120
121impl<T> Worker<T>
122where
123 T: ColumnIndexable + SSS,
124{
125 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}