1use matchmaker_partial_macros::partial;
5
6pub use crate::config_types::*;
7pub use crate::utils::{Percentage, serde::StringOrVec};
8
9use crate::{
10 MAX_SPLITS,
11 tui::IoStream,
12 utils::serde::{escaped_opt_char, escaped_opt_string, serde_duration_ms},
13};
14
15use cba::serde::transform::{camelcase_normalized, camelcase_normalized_option};
16use ratatui::{
17 style::{Color, Modifier, Style},
18 text::Span,
19 widgets::{BorderType, Borders},
20};
21
22use serde::{Deserialize, Serialize};
23
24#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
28#[partial(recurse, path, derive(Debug, Deserialize))]
29pub struct MatcherConfig {
30 #[serde(flatten)]
31 #[partial(skip)]
32 pub matcher: NucleoMatcherConfig,
33 #[serde(flatten)]
34 pub worker: WorkerConfig,
35}
36
37#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
41#[serde(default)]
42#[partial(path, derive(Debug, Clone, PartialEq, Deserialize, Serialize))]
43pub struct WorkerConfig {
44 #[serde(alias = "sort")]
46 pub sort_threshold: u32,
47 #[partial(alias = "r")]
49 pub raw: bool,
50 pub track: bool,
52 pub reverse: bool, }
55
56#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
59#[serde(default, deny_unknown_fields)]
60#[partial(path, derive(Debug, Clone, PartialEq, Deserialize, Serialize))]
61pub struct StartConfig {
62 #[serde(deserialize_with = "escaped_opt_char")]
63 #[partial(alias = "is")]
64 pub input_separator: Option<char>,
65 #[serde(deserialize_with = "escaped_opt_string")]
66 #[partial(alias = "os")]
67 pub output_separator: Option<String>,
68
69 #[partial(alias = "ot")]
71 #[serde(alias = "output")]
72 pub output_template: Option<String>,
73
74 #[partial(alias = "cmd", alias = "x")]
76 pub command: String,
77 #[partial(alias = "ax")]
79 pub additional_commands: Vec<String>,
80 pub sync: bool,
81
82 #[partial(alias = "a")]
84 pub ansi: bool,
85 #[partial(alias = "t")]
87 pub trim: bool,
88}
89
90#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
92#[serde(default, deny_unknown_fields)]
93#[partial(path, derive(Debug, Clone, PartialEq, Deserialize, Serialize))]
94pub struct ExitConfig {
95 pub select_1: bool,
97 pub allow_empty: bool,
99 pub abort_empty: bool,
101 pub last_key_path: Option<std::path::PathBuf>,
104}
105
106impl Default for ExitConfig {
107 fn default() -> Self {
108 Self {
109 select_1: false,
110 allow_empty: false,
111 abort_empty: true,
112 last_key_path: None,
113 }
114 }
115}
116
117#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
119#[serde(default, deny_unknown_fields)]
120#[partial(recurse, path, derive(Debug, Clone, PartialEq, Deserialize, Serialize))]
121pub struct RenderConfig {
122 pub ui: UiConfig,
124 #[partial(alias = "i")]
126 pub input: InputConfig,
127 #[partial(alias = "r")]
129 pub results: ResultsConfig,
130
131 pub status: StatusConfig,
133 #[partial(alias = "p")]
135 pub preview: PreviewConfig,
136 #[partial(alias = "f")]
137 pub footer: DisplayConfig,
138 #[partial(alias = "h")]
139 pub header: DisplayConfig,
140}
141
142impl RenderConfig {
143 pub fn tick_rate(&self) -> u8 {
144 self.ui.tick_rate
145 }
146}
147
148#[partial(path, derive(Debug, Clone, PartialEq, Deserialize, Serialize))]
150#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
151#[serde(default, deny_unknown_fields)]
152pub struct TerminalConfig {
153 pub stream: IoStream, pub restore_fullscreen: bool,
155 pub redraw_on_resize: bool,
156 pub extended_keys: bool,
158 #[serde(with = "serde_duration_ms")]
159 pub sleep_ms: std::time::Duration, #[serde(flatten)]
161 #[partial(recurse)]
162 pub layout: Option<TerminalLayoutSettings>, pub clear_on_exit: bool,
164 pub move_up_on_exit: bool,
166}
167
168impl Default for TerminalConfig {
169 fn default() -> Self {
170 Self {
171 stream: IoStream::default(),
172 restore_fullscreen: true,
173 redraw_on_resize: bool::default(),
174 sleep_ms: std::time::Duration::default(),
175 layout: Option::default(),
176 extended_keys: true,
177 clear_on_exit: true,
178 move_up_on_exit: false,
179 }
180 }
181}
182
183#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
185#[serde(default, deny_unknown_fields)]
186#[partial(path, derive(Debug, Clone, PartialEq, Deserialize, Serialize))]
187pub struct UiConfig {
188 #[partial(recurse)]
189 pub border: BorderSetting,
190 pub tick_rate: u8, }
192
193impl Default for UiConfig {
194 fn default() -> Self {
195 Self {
196 border: Default::default(),
197 tick_rate: 60,
198 }
199 }
200}
201
202#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
204#[serde(default, deny_unknown_fields)]
205#[partial(path, derive(Debug, Clone, PartialEq, Deserialize, Serialize))]
206pub struct InputConfig {
207 #[partial(recurse)]
208 pub border: BorderSetting,
209
210 #[serde(deserialize_with = "camelcase_normalized")]
212 pub fg: Color,
213 pub bg: Color,
214 pub modifier: Modifier,
216
217 #[serde(deserialize_with = "camelcase_normalized")]
218 pub prompt_fg: Color,
219 pub prompt_bg: Color,
220 pub prompt_modifier: Modifier,
222
223 #[serde(deserialize_with = "deserialize_string_or_char_as_double_width")]
225 pub prompt: String,
226 pub cursor: CursorSetting,
228
229 #[partial(alias = "i")]
231 pub initial: String,
232
233 pub scroll_padding: bool,
235}
236
237impl Default for InputConfig {
238 fn default() -> Self {
239 Self {
240 border: Default::default(),
241 fg: Default::default(),
242 bg: Default::default(),
243 modifier: Default::default(),
244 prompt_fg: Default::default(),
245 prompt_bg: Default::default(),
246 prompt_modifier: Default::default(),
247 prompt: "> ".to_string(),
248 cursor: Default::default(),
249 initial: Default::default(),
250
251 scroll_padding: true,
252 }
253 }
254}
255
256impl InputConfig {
257 pub fn text_style(&self) -> Style {
258 Style::default()
259 .fg(self.fg)
260 .bg(self.bg)
261 .remove_modifier(Modifier::all())
262 .add_modifier(self.modifier)
263 }
264
265 pub fn prompt_style(&self) -> Style {
266 Style::default()
267 .fg(self.prompt_fg)
268 .bg(self.prompt_bg)
269 .add_modifier(self.prompt_modifier)
270 }
271}
272
273#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
274#[serde(default, deny_unknown_fields)]
275#[partial(path, derive(Debug, Clone, PartialEq, Deserialize, Serialize))]
276pub struct OverlayConfig {
277 #[partial(recurse)]
278 pub border: BorderSetting,
279 pub outer_dim: bool,
280 pub layout: OverlayLayoutSettings,
281}
282
283#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
284#[partial(path, derive(Debug, Clone, PartialEq, Deserialize, Serialize))]
285pub struct OverlayLayoutSettings {
286 #[partial(alias = "p")]
288 pub percentage: [Percentage; 2],
289 pub min: [u16; 2],
291 pub max: [u16; 2],
293
294 pub y_offset: Percentage,
296}
297
298impl Default for OverlayLayoutSettings {
299 fn default() -> Self {
300 Self {
301 percentage: [Percentage::new(60), Percentage::new(30)],
302 min: [10, 10],
303 max: [200, 30],
304 y_offset: Percentage::new(55),
305 }
306 }
307}
308
309#[partial(path, derive(Debug, Clone, PartialEq, Deserialize, Serialize))]
312#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
313#[serde(default, deny_unknown_fields)]
314pub struct AutoscrollSettings {
315 #[partial(alias = "i")]
317 pub initial_preserved: usize,
318 #[partial(alias = "a")]
320 pub enabled: bool,
321 #[partial(alias = "c")]
323 pub context: usize,
324 #[partial(alias = "e")]
326 pub end: bool,
327}
328
329impl Default for AutoscrollSettings {
330 fn default() -> Self {
331 Self {
332 initial_preserved: 0,
333 enabled: true,
334 context: 4,
335 end: false,
336 }
337 }
338}
339
340#[partial(path, derive(Debug, Clone, PartialEq, Deserialize, Serialize))]
341#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
342#[serde(default, deny_unknown_fields)]
343pub struct ResultsConfig {
344 #[partial(recurse)]
345 pub border: BorderSetting,
346
347 #[serde(deserialize_with = "deserialize_string_or_char_as_double_width")]
349 pub multi_prefix: String,
350 pub default_prefix: String,
351
352 pub multi: bool,
354
355 #[serde(deserialize_with = "camelcase_normalized")]
357 pub fg: Color,
358 #[serde(deserialize_with = "camelcase_normalized")]
359 pub bg: Color,
360 pub modifier: Modifier,
362
363 #[serde(deserialize_with = "camelcase_normalized")]
365 pub inactive_fg: Color,
366 #[serde(deserialize_with = "camelcase_normalized")]
367 pub inactive_bg: Color,
368 pub inactive_modifier: Modifier,
370
371 #[serde(deserialize_with = "camelcase_normalized")]
373 pub inactive_current_fg: Color,
374 #[serde(deserialize_with = "camelcase_normalized")]
375 pub inactive_current_bg: Color,
376 pub inactive_current_modifier: Modifier,
378
379 #[serde(deserialize_with = "camelcase_normalized")]
380 pub match_fg: Color,
381 pub match_modifier: Modifier,
383
384 #[serde(deserialize_with = "camelcase_normalized")]
386 pub current_fg: Color,
387 #[serde(deserialize_with = "camelcase_normalized")]
389 pub current_bg: Color,
390 pub current_modifier: Modifier,
393
394 #[serde(deserialize_with = "camelcase_normalized")]
399 pub row_connection: RowConnectionStyle,
400
401 #[partial(alias = "c")]
403 #[serde(alias = "cycle")]
404 pub scroll_wrap: bool,
405 #[partial(alias = "sp")]
406 pub scroll_padding: u16,
407 #[partial(alias = "r")]
408 pub reverse: Option<bool>,
409
410 #[partial(alias = "w")]
412 pub wrap: bool,
413 pub min_wrap_width: u16,
414
415 #[partial(recurse, alias = "a")]
417 pub autoscroll: AutoscrollSettings,
418
419 pub column_spacing: Count,
423 pub current_prefix: String,
424
425 #[partial(alias = "ra")]
427 pub right_align_last: bool,
428
429 #[partial(alias = "v")]
430 #[serde(alias = "vertical")]
431 pub stacked_columns: bool,
432
433 #[serde(alias = "hr")]
434 #[serde(deserialize_with = "camelcase_normalized")]
435 pub horizontal_separator: HorizontalSeparator,
436}
437
438impl Default for ResultsConfig {
439 fn default() -> Self {
440 ResultsConfig {
441 border: Default::default(),
442
443 multi_prefix: "▌ ".to_string(),
444 default_prefix: Default::default(),
445 multi: true,
446
447 fg: Default::default(),
448 modifier: Default::default(),
449 bg: Default::default(),
450
451 inactive_fg: Default::default(),
452 inactive_modifier: Default::default(),
453 inactive_bg: Default::default(),
454
455 inactive_current_fg: Color::DarkGray,
456 inactive_current_modifier: Default::default(),
457 inactive_current_bg: Color::Black,
458
459 match_fg: Color::Green,
460 match_modifier: Modifier::ITALIC,
461
462 current_fg: Default::default(),
463 current_bg: Color::Black,
464 current_modifier: Modifier::BOLD,
465 row_connection: RowConnectionStyle::Capped,
466
467 scroll_wrap: true,
468 scroll_padding: 2,
469 reverse: Default::default(),
470
471 wrap: Default::default(),
472 min_wrap_width: 4,
473
474 autoscroll: Default::default(),
475
476 column_spacing: Default::default(),
477 current_prefix: Default::default(),
478 right_align_last: false,
479 stacked_columns: false,
480 horizontal_separator: Default::default(),
481 }
482 }
483}
484
485#[partial(path, derive(Debug, Clone, PartialEq, Deserialize, Serialize))]
486#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
487#[serde(default, deny_unknown_fields)]
488pub struct StatusConfig {
489 #[serde(deserialize_with = "camelcase_normalized")]
490 pub fg: Color,
491 #[serde(deserialize_with = "camelcase_normalized")]
492 pub bg: Color,
493 pub modifier: Modifier,
495
496 pub show: bool,
498 pub match_indent: bool,
500
501 #[partial(alias = "t")]
507 pub template: String,
508
509 pub row_connection: RowConnectionStyle,
513}
514impl Default for StatusConfig {
515 fn default() -> Self {
516 Self {
517 fg: Color::Green,
518 bg: Default::default(),
519 modifier: Modifier::ITALIC,
520 show: true,
521 match_indent: true,
522 template: r#"\m/\t"#.to_string(),
523 row_connection: RowConnectionStyle::Full,
524 }
525 }
526}
527
528impl StatusConfig {
529 pub fn base_style(&self) -> Style {
530 Style::default()
531 .fg(self.fg)
532 .bg(self.bg)
533 .add_modifier(self.modifier)
534 }
535}
536
537#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
538#[serde(default, deny_unknown_fields)]
539#[partial(path, derive(Debug, Clone, PartialEq, Deserialize, Serialize))]
540pub struct DisplayConfig {
541 #[partial(recurse)]
542 pub border: BorderSetting,
543
544 #[serde(deserialize_with = "camelcase_normalized")]
545 pub fg: Color,
546 pub modifier: Modifier,
548
549 pub match_indent: bool,
551 pub wrap: bool,
553
554 pub content: Option<StringOrVec>,
556
557 #[serde(deserialize_with = "camelcase_normalized")]
566 pub row_connection: RowConnectionStyle,
567
568 #[partial(alias = "h")]
570 pub header_lines: usize,
571}
572
573impl Default for DisplayConfig {
574 fn default() -> Self {
575 DisplayConfig {
576 border: Default::default(),
577 match_indent: true,
578 fg: Color::Green,
579 wrap: false,
580 row_connection: Default::default(),
581 modifier: Modifier::ITALIC, content: None,
583 header_lines: 0,
584 }
585 }
586}
587
588#[partial(path, derive(Debug, Clone, PartialEq, Deserialize, Serialize))]
604#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
605#[serde(default, deny_unknown_fields)]
606pub struct PreviewConfig {
607 #[partial(recurse)]
608 pub border: BorderSetting,
609 #[partial(recurse, set = "recurse")]
610 #[partial(alias = "l")]
611 pub layout: Vec<PreviewSetting>,
612 #[serde(alias = "scroll")]
613 #[partial(recurse)]
614 #[partial(alias = "i")]
615 pub initial: PreviewInitialSetting,
616 #[partial(alias = "c")]
618 #[serde(alias = "cycle")]
619 pub scroll_wrap: bool,
620 pub wrap: bool,
621 pub show: ShowCondition,
624
625 pub reevaluate_show_on_resize: bool,
626}
627
628impl Default for PreviewConfig {
629 fn default() -> Self {
630 PreviewConfig {
631 border: BorderSetting {
632 padding: Padding(ratatui::widgets::Padding::left(2)),
633 ..Default::default()
634 },
635 initial: Default::default(),
636 layout: Default::default(),
637 scroll_wrap: true,
638 wrap: Default::default(),
639 show: Default::default(),
640 reevaluate_show_on_resize: false,
641 }
642 }
643}
644
645#[partial(path, derive(Debug, Clone, PartialEq, Deserialize, Serialize))]
647#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
648#[serde(default, deny_unknown_fields)]
649pub struct PreviewInitialSetting {
650 pub index: Option<StringValue>,
653 #[partial(alias = "o")]
655 pub offset: isize,
656 #[partial(alias = "p")]
658 pub percentage: Percentage,
659 #[partial(alias = "h")]
661 pub header_lines: usize,
662}
663
664impl Default for PreviewInitialSetting {
665 fn default() -> Self {
666 Self {
667 index: Default::default(),
668 offset: -1,
669 percentage: Default::default(),
670 header_lines: Default::default(),
671 }
672 }
673}
674
675#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
676#[serde(default, deny_unknown_fields)]
677pub struct PreviewerConfig {
678 pub try_lossy: bool,
679
680 pub cache: u8,
682
683 pub help_colors: HelpColorConfig,
684}
685
686#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
688pub struct HelpColorConfig {
689 #[serde(deserialize_with = "camelcase_normalized")]
690 pub section: Color,
691 #[serde(deserialize_with = "camelcase_normalized")]
692 pub key: Color,
693 #[serde(deserialize_with = "camelcase_normalized")]
694 pub value: Color,
695}
696
697impl Default for HelpColorConfig {
698 fn default() -> Self {
699 Self {
700 section: Color::Blue,
701 key: Color::Green,
702 value: Color::White,
703 }
704 }
705}
706
707#[derive(Default, Debug, Clone, PartialEq, Deserialize, Serialize)]
710#[partial(path, derive(Debug, Clone, PartialEq, Deserialize, Serialize))]
711#[serde(default, deny_unknown_fields)]
712pub struct BorderSetting {
713 #[serde(deserialize_with = "camelcase_normalized_option")]
714 pub r#type: Option<BorderType>,
715 #[serde(deserialize_with = "camelcase_normalized")]
716 pub color: Color,
717 pub sides: Option<Borders>,
726 pub padding: Padding,
734 pub title: String,
735 pub title_modifier: Modifier,
737 pub modifier: Modifier,
738 #[serde(deserialize_with = "camelcase_normalized")]
739 pub bg: Color,
740}
741
742impl BorderSetting {
743 pub fn as_block(&self) -> ratatui::widgets::Block<'_> {
744 let mut ret = ratatui::widgets::Block::default()
745 .padding(self.padding.0)
746 .style(Style::default().bg(self.bg).add_modifier(self.modifier));
747
748 if !self.title.is_empty() {
749 let title = Span::styled(
750 &self.title,
751 Style::default().add_modifier(self.title_modifier),
752 );
753
754 ret = ret.title(title)
755 };
756
757 if !self.is_empty() {
758 ret = ret
759 .borders(self.sides())
760 .border_type(self.r#type.unwrap_or_default())
761 .border_style(ratatui::style::Style::default().fg(self.color))
762 }
763
764 ret
765 }
766
767 pub fn sides(&self) -> Borders {
768 if let Some(s) = self.sides {
769 s
770 } else if self.color != Default::default() || self.r#type != Default::default() {
771 Borders::ALL
772 } else {
773 Borders::NONE
774 }
775 }
776
777 pub fn as_static_block(&self) -> ratatui::widgets::Block<'static> {
778 let mut ret = ratatui::widgets::Block::default()
779 .padding(self.padding.0)
780 .style(Style::default().bg(self.bg).add_modifier(self.modifier));
781
782 if !self.title.is_empty() {
783 let title: Span<'static> = Span::styled(
784 self.title.clone(),
785 Style::default().add_modifier(self.title_modifier),
786 );
787
788 ret = ret.title(title)
789 };
790
791 if !self.is_empty() {
792 ret = ret
793 .borders(self.sides())
794 .border_type(self.r#type.unwrap_or_default())
795 .border_style(ratatui::style::Style::default().fg(self.color))
796 }
797
798 ret
799 }
800
801 pub fn is_empty(&self) -> bool {
802 self.sides() == Borders::NONE
803 }
804
805 pub fn height(&self) -> u16 {
806 let mut height = 0;
807 height += 2 * !self.is_empty() as u16;
808 height += self.padding.top + self.padding.bottom;
809 height += (!self.title.is_empty() as u16).saturating_sub(!self.is_empty() as u16);
810
811 height
812 }
813
814 pub fn width(&self) -> u16 {
815 let mut width = 0;
816 width += 2 * !self.is_empty() as u16;
817 width += self.padding.left + self.padding.right;
818
819 width
820 }
821
822 pub fn left(&self) -> u16 {
823 let mut width = 0;
824 width += !self.is_empty() as u16;
825 width += self.padding.left;
826
827 width
828 }
829
830 pub fn top(&self) -> u16 {
831 let mut height = 0;
832 height += !self.is_empty() as u16;
833 height += self.padding.top;
834 height += (!self.title.is_empty() as u16).saturating_sub(!self.is_empty() as u16);
835
836 height
837 }
838}
839
840#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
842#[partial(path, derive(Debug, Clone, PartialEq, Deserialize, Serialize))]
843pub struct TerminalLayoutSettings {
844 #[partial(alias = "p")]
846 pub percentage: Percentage,
847 pub min: u16,
848 pub max: u16, }
850
851impl Default for TerminalLayoutSettings {
852 fn default() -> Self {
853 Self {
854 percentage: Percentage::new(50),
855 min: 10,
856 max: 120,
857 }
858 }
859}
860
861#[partial(path, derive(Debug, Clone, PartialEq, Deserialize, Serialize))]
862#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
863#[serde(default)]
864pub struct PreviewSetting {
865 #[serde(flatten)]
866 #[partial(recurse)]
867 pub layout: PreviewLayout,
868 #[partial(recurse)]
869 pub border: Option<BorderSetting>,
870 #[serde(default, alias = "cmd", alias = "x")]
871 pub command: String,
872}
873
874#[partial(path, derive(Debug, Clone, PartialEq, Deserialize, Serialize))]
875#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
876#[serde(default)]
877pub struct PreviewLayout {
878 pub side: Side,
879 #[serde(alias = "p")]
881 pub percentage: Percentage,
883 pub min: i16,
884 pub max: i16,
885}
886
887impl Default for PreviewLayout {
888 fn default() -> Self {
889 Self {
890 side: Side::Right,
891 percentage: Percentage::new(60),
892 min: 15,
893 max: 120,
894 }
895 }
896}
897
898use crate::utils::serde::bounded_usize;
899#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
900#[serde(default, deny_unknown_fields)]
901#[partial(path, derive(Debug, Clone, PartialEq, Deserialize, Serialize))]
902pub struct ColumnsConfig {
903 #[partial(alias = "s")]
905 pub split: Split,
906 #[partial(alias = "n")]
908 pub names: Vec<ColumnSetting>,
909 #[serde(deserialize_with = "bounded_usize::<_, 1, {crate::MAX_SPLITS}>")]
911 #[partial(alias = "mc")]
912 max: usize,
913 #[partial(alias = "i")]
914 pub default: Option<StringValue>,
915 pub names_from_zero: bool,
917}
918
919impl ColumnsConfig {
920 pub fn max_cols(&self) -> usize {
921 self.max.min(MAX_SPLITS).max(1)
922 }
923}
924
925impl Default for ColumnsConfig {
926 fn default() -> Self {
927 Self {
928 split: Default::default(),
929 names: Default::default(),
930 max: 6,
931 default: None,
932 names_from_zero: false,
933 }
934 }
935}
936
937#[derive(Debug, Clone, PartialEq)]
939pub struct NucleoMatcherConfig(pub nucleo::Config);
940
941impl Default for NucleoMatcherConfig {
942 fn default() -> Self {
943 Self(nucleo::Config::DEFAULT)
944 }
945}
946
947#[derive(Debug, Clone, Serialize, Deserialize)]
948#[serde(default)]
949#[derive(Default)]
950struct MatcherConfigHelper {
951 pub normalize: Option<bool>,
952 pub ignore_case: Option<bool>,
953 pub prefer_prefix: Option<bool>,
954}
955
956impl serde::Serialize for NucleoMatcherConfig {
957 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
958 where
959 S: serde::Serializer,
960 {
961 let helper = MatcherConfigHelper {
962 normalize: Some(self.0.normalize),
963 ignore_case: Some(self.0.ignore_case),
964 prefer_prefix: Some(self.0.prefer_prefix),
965 };
966 helper.serialize(serializer)
967 }
968}
969
970impl<'de> Deserialize<'de> for NucleoMatcherConfig {
971 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
972 where
973 D: serde::Deserializer<'de>,
974 {
975 let helper = MatcherConfigHelper::deserialize(deserializer)?;
976 let mut config = nucleo::Config::DEFAULT;
977
978 if let Some(norm) = helper.normalize {
979 config.normalize = norm;
980 }
981 if let Some(ic) = helper.ignore_case {
982 config.ignore_case = ic;
983 }
984 if let Some(pp) = helper.prefer_prefix {
985 config.prefer_prefix = pp;
986 }
987
988 Ok(NucleoMatcherConfig(config))
989 }
990}