1use std::{fmt, ops::Deref};
5
6use crate::MAX_SPLITS;
7use crate::{Result, action::{Count}, tui::IoStream};
8
9use ratatui::style::Style;
10use ratatui::text::{Span};
11use ratatui::{
12 style::{Color, Modifier}, widgets::{BorderType, Borders, Padding}
13};
14use regex::Regex;
15use serde::{Serialize, Serializer};
16use serde::de::IntoDeserializer;
17use serde::ser::SerializeSeq;
18use serde::{Deserialize, Deserializer, de::{self, Visitor}};
19
20pub use crate::utils::{Percentage, serde::{StringOrVec, escaped_opt_char, escaped_opt_string}};
21
22#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
26pub struct MatcherConfig {
27 #[serde(flatten)]
28 pub matcher: NucleoMatcherConfig,
29 #[serde(flatten)]
30 pub worker: WorkerConfig,
31 #[serde(flatten)]
33 pub start: StartConfig,
34}
35
36#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
40#[serde(default)]
41pub struct WorkerConfig {
42 pub columns: ColumnsConfig,
43 #[serde(flatten)]
44 pub exit: ExitConfig,
45 pub trim: bool, pub format: FormatString, }
48
49#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
53#[serde(default)]
54pub struct StartConfig {
55 #[serde(default, deserialize_with = "escaped_opt_char")]
56 pub input_separator: Option<char>,
57 #[serde(default, deserialize_with = "escaped_opt_string")]
58 pub output_separator: Option<String>,
59 pub default_command: String,
60 pub sync: bool
61}
62
63
64#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
68#[serde(default)]
69pub struct ExitConfig {
70 pub select_1: bool,
71 pub allow_empty: bool,
72}
73
74#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
76#[serde(default, deny_unknown_fields)]
77pub struct RenderConfig {
78 pub ui: UiConfig,
79 pub input: InputConfig,
80 pub results: ResultsConfig,
81 pub preview: PreviewConfig,
82 pub footer: DisplayConfig,
83 pub header: DisplayConfig,
84 pub overlay: Option<OverlayConfig>
85 }
87
88impl RenderConfig {
89 pub fn tick_rate(&self) -> u8 {
90 self.ui.tick_rate
91 }
92}
93
94#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
96#[serde(default, deny_unknown_fields)]
97pub struct TerminalConfig {
98 pub stream: IoStream, pub restore_fullscreen: bool,
100 pub redraw_on_resize: bool,
101 pub sleep: u64, #[serde(flatten)]
104 pub layout: Option<TerminalLayoutSettings> }
106#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
107#[serde(default, deny_unknown_fields)]
108pub struct TerminalSettings {
109
110}
111
112
113#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
115#[serde(default, deny_unknown_fields)]
116pub struct UiConfig {
117 pub border: BorderSetting,
118 pub tick_rate: u8, }
120
121impl Default for UiConfig {
122 fn default() -> Self {
123 Self {
124 border: Default::default(),
125 tick_rate: 60,
126 }
127 }
128}
129
130#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
132#[serde(default, deny_unknown_fields)]
133pub struct InputConfig {
134 pub border: BorderSetting,
135
136 pub fg: Color,
138 #[serde(deserialize_with = "deserialize_modifier", serialize_with = "serialize_modifier")]
139 pub modifier: Modifier,
140
141 pub prompt_fg: Color,
142 #[serde(deserialize_with = "deserialize_modifier", serialize_with = "serialize_modifier")]
143 pub prompt_modifier: Modifier,
144
145 #[serde(deserialize_with = "deserialize_string_or_char_as_double_width")]
146 pub prompt: String,
147 pub cursor: CursorSetting,
148 pub initial: String,
149}
150
151impl Default for InputConfig {
152 fn default() -> Self {
153 Self {
154 border: Default::default(),
155 fg: Default::default(),
156 modifier: Default::default(),
157 prompt_fg: Default::default(),
158 prompt_modifier: Default::default(),
159 prompt: "> ".to_string(),
160 cursor: Default::default(),
161 initial: Default::default(),
162 }
163 }
164}
165
166#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
167#[serde(default, deny_unknown_fields)]
168pub struct OverlayConfig {
169 pub border: BorderSetting,
170 pub outer_dim: bool,
171 pub layout: OverlayLayoutSettings,
172}
173
174
175#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
176pub struct OverlayLayoutSettings {
177 pub percentage: [Percentage; 2],
179 pub min: [u16; 2],
181 pub max: [u16; 2],
183}
184
185impl Default for OverlayLayoutSettings {
186 fn default() -> Self {
187 Self {
188 percentage: [Percentage::new(60), Percentage::new(30)],
189 min: [10, 5],
190 max: [200, 30]
191 }
192 }
193}
194
195#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
198#[serde(default, deny_unknown_fields)]
199pub struct ResultsConfig {
200 pub border: BorderSetting,
201
202 #[serde(deserialize_with = "deserialize_string_or_char_as_double_width")]
204 pub multi_prefix: String,
205 pub default_prefix: String,
206
207 pub fg: Color,
209 #[serde(deserialize_with = "deserialize_modifier", serialize_with = "serialize_modifier")]
210 pub modifier: Modifier,
211
212 #[serde(default = "default_green")]
213 pub match_fg: Color,
214 #[serde(deserialize_with = "deserialize_modifier", serialize_with = "serialize_modifier")]
215 pub match_modifier: Modifier,
216
217 pub current_fg: Color,
218 #[serde(default = "default_black")]
219 pub current_bg: Color,
220 #[serde(deserialize_with = "deserialize_modifier", serialize_with = "serialize_modifier", default = "default_bold")]
221 pub current_modifier: Modifier,
222
223 #[serde(default = "default_green")]
225 pub count_fg: Color,
226 #[serde(deserialize_with = "deserialize_modifier", serialize_with = "serialize_modifier", default = "default_italic")]
227 pub count_modifier: Modifier,
228
229 #[serde(default = "default_true")]
236 pub scroll_wrap: bool,
237 pub scroll_padding: u16,
238 #[serde(deserialize_with = "deserialize_option_auto")]
239 pub reverse: Option<bool>,
240
241 pub wrap: bool,
243 pub wrap_scaling_min_width: u8,
244
245 pub column_spacing: Count,
247 pub current_prefix: String,
248 pub right_align_last: bool
249}
250
251impl Default for ResultsConfig {
252 fn default() -> Self {
253 ResultsConfig {
254 border: Default::default(),
255
256 multi_prefix: "▌ ".to_string(),
257 default_prefix: Default::default(),
258
259 fg: Default::default(),
260 modifier: Default::default(), match_fg: default_green(),
262 match_modifier: default_italic(),
263
264 current_fg: Default::default(),
265 current_bg: default_black(),
266 current_modifier: default_bold(),
267
268 count_fg: default_green(),
269 count_modifier: default_italic(),
270
271 scroll_wrap: default_true(),
272 scroll_padding: 2,
273 reverse: None,
274
275 wrap: Default::default(),
276 wrap_scaling_min_width: 5,
277
278 column_spacing: Default::default(),
279 current_prefix: Default::default(),
280 right_align_last: true
281 }
282 }
283}
284
285
286#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
287#[serde(default, deny_unknown_fields)]
288pub struct DisplayConfig {
289 pub border: BorderSetting,
290
291 #[serde(default = "default_green")]
292 pub fg: Color,
293 #[serde(deserialize_with = "deserialize_modifier", serialize_with = "serialize_modifier", default = "default_italic")]
294 pub modifier: Modifier,
295
296 #[serde(default = "default_true")]
297 pub match_indent: bool,
298 pub wrap: bool,
299 #[serde(deserialize_with = "deserialize_option_auto")]
300 pub content: Option<StringOrVec>,
301}
302
303impl Default for DisplayConfig {
304 fn default() -> Self {
305 DisplayConfig {
306 border: Default::default(),
307 match_indent: true,
308 fg: default_green(),
309 wrap: false,
310 modifier: default_italic(), content: None,
312 }
313 }
314}
315
316
317#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
318#[serde(default, deny_unknown_fields)]
319pub struct PreviewConfig {
320 pub border: BorderSetting,
321
322 pub layout: Vec<PreviewSetting>,
323 #[serde(default = "default_true")]
324 pub scroll_wrap: bool,
325 pub wrap: bool,
326 pub show: bool,
327}
328
329impl Default for PreviewConfig {
330 fn default() -> Self {
331 PreviewConfig {
332 border: Default::default(),
333 layout: Default::default(),
340 scroll_wrap: default_true(),
341 wrap: Default::default(),
342 show: Default::default(),
343 }
344 }
345}
346
347#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
348#[serde(default, deny_unknown_fields)]
349pub struct PreviewerConfig {
350 pub try_lossy: bool,
351
352 pub cache: u8,
354
355 #[serde(default)]
356 pub help_colors: TomlColorConfig
357}
358
359#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
361pub struct TomlColorConfig {
362 pub section: Color,
363 pub key: Color,
364 pub string: Color,
365 pub number: Color,
366 pub section_bold: bool,
367}
368
369impl Default for TomlColorConfig {
370 fn default() -> Self {
371 Self {
372 section: Color::Blue,
373 key: Color::Yellow,
374 string: Color::Green,
375 number: Color::Cyan,
376 section_bold: true,
377 }
378 }
379}
380
381#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
385#[serde(transparent)]
386pub struct FormatString(String);
387
388impl Deref for FormatString {
389 type Target = str;
390
391 fn deref(&self) -> &Self::Target {
392 &self.0
393 }
394}
395
396#[derive(Default, Debug, Clone, PartialEq, Serialize)]
397#[serde(default, deny_unknown_fields)]
398pub struct BorderSetting {
399 #[serde(with = "crate::utils::serde::fromstr")]
400 pub r#type: BorderType,
401 pub color: Color,
402 #[serde(serialize_with = "serialize_borders", deserialize_with = "deserialize_borders")]
403 pub sides: Borders,
404 #[serde(serialize_with = "serialize_padding", deserialize_with = "deserialize_padding")]
405 pub padding: Padding,
406 pub title: String,
407 #[serde(deserialize_with = "deserialize_modifier", serialize_with = "serialize_modifier")]
408 pub title_modifier: Modifier,
409 pub bg: Color,
410}
411
412impl BorderSetting {
413 pub fn as_block(&self) -> ratatui::widgets::Block<'_> {
414 let mut ret = ratatui::widgets::Block::default()
415 .padding(self.padding)
416 .style(Style::default().bg(self.bg));
417
418 if !self.title.is_empty() {
419 let title = Span::styled(&self.title, Style::default().add_modifier(self.title_modifier));
420
421 ret = ret.title(title)
422 };
423
424 if self.sides != Borders::NONE {
425 ret = ret.borders(self.sides)
426 .border_type(self.r#type)
427 .border_style(
428 ratatui::style::Style::default()
429 .fg(self.color)
430 )
431 }
432
433 ret
434 }
435
436 pub fn as_static_block(&self) -> ratatui::widgets::Block<'static> {
437 let mut ret = ratatui::widgets::Block::default()
438 .padding(self.padding)
439 .style(Style::default().bg(self.bg));
440
441 if !self.title.is_empty() {
442 let title: Span<'static> = Span::styled(self.title.clone(), Style::default().add_modifier(self.title_modifier));
443
444 ret = ret.title(title)
445 };
446
447 if self.sides != Borders::NONE {
448 ret = ret.borders(self.sides)
449 .border_type(self.r#type)
450 .border_style(
451 ratatui::style::Style::default()
452 .fg(self.color)
453 )
454 }
455
456 ret
457 }
458
459 pub fn height(&self) -> u16 {
460 let mut height = 0;
461 height += 2 * !self.sides.is_empty() as u16;
462 height += self.padding.top + self.padding.bottom;
463 height += (!self.title.is_empty() as u16).saturating_sub(!self.sides.is_empty() as u16);
464
465 height
466 }
467
468 pub fn width(&self) -> u16 {
469 let mut width = 0;
470 width += 2 * !self.sides.is_empty() as u16;
471 width += self.padding.left + self.padding.right;
472
473 width
474 }
475
476 pub fn left(&self) -> u16 {
477 let mut width = 0;
478 width += !self.sides.is_empty() as u16;
479 width += self.padding.left;
480
481 width
482 }
483
484 pub fn top(&self) -> u16 {
485 let mut height = 0;
486 height += !self.sides.is_empty() as u16;
487 height += self.padding.top;
488 height += (!self.title.is_empty() as u16).saturating_sub(!self.sides.is_empty() as u16);
489
490 height
491 }
492}
493
494#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
496pub struct TerminalLayoutSettings {
497 pub percentage: Percentage,
498 pub min: u16,
499 pub max: u16, }
501
502impl Default for TerminalLayoutSettings {
503 fn default() -> Self {
504 Self {
505 percentage: Percentage::new(50),
506 min: 10,
507 max: 120
508 }
509 }
510}
511
512
513#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
514#[serde(rename_all = "lowercase")]
515pub enum Side {
516 Top,
517 Bottom,
518 Left,
519 #[default]
520 Right,
521}
522
523#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
524pub struct PreviewSetting {
525 #[serde(flatten)]
526 pub layout: PreviewLayoutSetting,
527 #[serde(default)]
528 pub command: String
529}
530
531#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
532pub struct PreviewLayoutSetting {
533 pub side: Side,
534 pub percentage: Percentage,
535 pub min: i16,
536 pub max: i16,
537}
538
539impl Default for PreviewLayoutSetting {
540 fn default() -> Self {
541 Self {
542 side: Side::Right,
543 percentage: Percentage::new(60),
544 min: 30,
545 max: 120
546 }
547 }
548}
549
550
551#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
552#[serde(rename_all = "lowercase")]
553pub enum CursorSetting {
554 None,
555 #[default]
556 Default,
557}
558
559use crate::utils::serde::bounded_usize;
560#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
562#[serde(default, deny_unknown_fields)]
563pub struct ColumnsConfig {
564 pub split: Split,
565 pub names: Vec<ColumnSetting>,
566 #[serde(deserialize_with = "bounded_usize::<{crate::MAX_SPLITS},_>")]
567 max_columns: usize,
568}
569
570impl ColumnsConfig {
571 pub fn max_cols(&self) -> usize {
572 self.max_columns.min(MAX_SPLITS)
573 }
574}
575
576#[derive(Default, Debug, Clone, PartialEq)]
577pub struct ColumnSetting {
578 pub filter: bool,
579 pub hidden: bool,
580 pub name: String,
581}
582
583#[derive(Default, Debug, Clone)]
584pub enum Split {
585 Delimiter(Regex),
586 Regexes(Vec<Regex>),
587 #[default]
588 None
589}
590
591impl PartialEq for Split {
592 fn eq(&self, other: &Self) -> bool {
593 match (self, other) {
594 (Split::Delimiter(r1), Split::Delimiter(r2)) => r1.as_str() == r2.as_str(),
595 (Split::Regexes(v1), Split::Regexes(v2)) => {
596 if v1.len() != v2.len() { return false; }
597 v1.iter().zip(v2.iter()).all(|(r1, r2)| r1.as_str() == r2.as_str())
598 },
599 (Split::None, Split::None) => true,
600 _ => false,
601 }
602 }
603}
604
605
606pub fn serialize_borders<S>(borders: &Borders, serializer: S) -> Result<S::Ok, S::Error>
608where
609S: serde::Serializer,
610{
611 use serde::ser::SerializeSeq;
612 let mut seq = serializer.serialize_seq(None)?;
613 if borders.contains(Borders::TOP) {
614 seq.serialize_element("top")?;
615 }
616 if borders.contains(Borders::BOTTOM) {
617 seq.serialize_element("bottom")?;
618 }
619 if borders.contains(Borders::LEFT) {
620 seq.serialize_element("left")?;
621 }
622 if borders.contains(Borders::RIGHT) {
623 seq.serialize_element("right")?;
624 }
625 seq.end()
626}
627
628pub fn deserialize_borders<'de, D>(deserializer: D) -> Result<Borders, D::Error>
629where
630D: Deserializer<'de>,
631{
632 let input = StringOrVec::deserialize(deserializer)?;
633 let mut borders = Borders::NONE;
634
635 let borders = match input {
636 StringOrVec::String(s) => match s.as_str() {
637 "none" => Borders::NONE,
638 "all" => Borders::ALL,
639 other => {
640 return Err(de::Error::custom(format!(
641 "invalid border value '{}'",
642 other
643 )))
644 }
645 },
646 StringOrVec::Vec(list) => {
647 for item in list {
648 match item.as_str() {
649 "top" => borders |= Borders::TOP,
650 "bottom" => borders |= Borders::BOTTOM,
651 "left" => borders |= Borders::LEFT,
652 "right" => borders |= Borders::RIGHT,
653 "all" => borders |= Borders::ALL,
654 "none" => borders = Borders::NONE,
655 other => return Err(de::Error::custom(format!("invalid side '{}'", other))),
656 }
657 }
658 borders
659 }
660 };
661
662 Ok(borders)
663}
664
665pub fn deserialize_borders_option<'de, D>(deserializer: D) -> Result<Option<Borders>, D::Error>
666where
667D: Deserializer<'de>,
668{
669 let input = Option::<StringOrVec>::deserialize(deserializer)?;
670 match input {
671 Some(input) => {
672 let mut borders = Borders::NONE;
673
674 let borders = match input {
675 StringOrVec::String(s) => match s.as_str() {
676 "none" => Borders::NONE,
677 "all" => Borders::ALL,
678 other => {
679 return Err(de::Error::custom(format!(
680 "invalid border value '{}'",
681 other
682 )))
683 }
684 },
685 StringOrVec::Vec(list) => {
686 for item in list {
687 match item.as_str() {
688 "top" => borders |= Borders::TOP,
689 "bottom" => borders |= Borders::BOTTOM,
690 "left" => borders |= Borders::LEFT,
691 "right" => borders |= Borders::RIGHT,
692 "all" => borders |= Borders::ALL,
693 "none" => borders = Borders::NONE,
694 other => return Err(de::Error::custom(format!("invalid side '{}'", other))),
695 }
696 }
697 borders
698 }
699 };
700
701 Ok(Some(borders))
702 },
703 None => Ok(None),
704 }
705}
706
707pub fn deserialize_modifier<'de, D>(deserializer: D) -> Result<Modifier, D::Error>
708where
709D: Deserializer<'de>,
710{
711 let input = StringOrVec::deserialize(deserializer)?;
712 let mut modifier = Modifier::empty();
713
714 let add_modifier = |name: &str, m: &mut Modifier| -> Result<(), D::Error> {
715 match name.to_lowercase().as_str() {
716 "bold" => {
717 *m |= Modifier::BOLD;
718 Ok(())
719 }
720 "italic" => {
721 *m |= Modifier::ITALIC;
722 Ok(())
723 }
724 "underlined" => {
725 *m |= Modifier::UNDERLINED;
726 Ok(())
727 }
728 "none" => {
749 *m = Modifier::empty();
750 Ok(())
751 } other => Err(de::Error::custom(format!("invalid modifier '{}'", other))),
753 }
754 };
755
756 match input {
757 StringOrVec::String(s) => add_modifier(&s, &mut modifier)?,
758 StringOrVec::Vec(list) => {
759 for item in list {
760 add_modifier(&item, &mut modifier)?;
761 }
762 }
763 }
764
765 Ok(modifier)
766}
767
768pub fn serialize_modifier<S>(modifier: &Modifier, serializer: S) -> Result<S::Ok, S::Error>
769where
770S: Serializer,
771{
772 let mut mods = Vec::new();
773
774 if modifier.contains(Modifier::BOLD) {
775 mods.push("bold");
776 }
777 if modifier.contains(Modifier::ITALIC) {
778 mods.push("italic");
779 }
780 if modifier.contains(Modifier::UNDERLINED) {
781 mods.push("underlined");
782 }
783 match mods.len() {
787 0 => serializer.serialize_str("none"),
788 1 => serializer.serialize_str(mods[0]),
789 _ => mods.serialize(serializer),
790 }
791}
792
793
794pub fn deserialize_string_or_char_as_double_width<'de, D, T>(deserializer: D) -> Result<T, D::Error>
795where
796D: Deserializer<'de>,
797T: From<String>,
798{
799 struct GenericVisitor<T> {
800 _marker: std::marker::PhantomData<T>,
801 }
802
803 impl<'de, T> Visitor<'de> for GenericVisitor<T>
804 where
805 T: From<String>,
806 {
807 type Value = T;
808
809 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
810 formatter.write_str("a string or single character")
811 }
812
813 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
814 where
815 E: de::Error,
816 {
817 let s = if v.chars().count() == 1 {
818 let mut s = String::with_capacity(2);
819 s.push(v.chars().next().unwrap());
820 s.push(' ');
821 s
822 } else {
823 v.to_string()
824 };
825 Ok(T::from(s))
826 }
827
828 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
829 where
830 E: de::Error,
831 {
832 self.visit_str(&v)
833 }
834 }
835
836 deserializer.deserialize_string(GenericVisitor { _marker: std::marker::PhantomData })
837}
838
839
840#[derive(Debug, Clone, PartialEq)]
842pub struct NucleoMatcherConfig(pub nucleo::Config);
843
844impl Default for NucleoMatcherConfig {
845 fn default() -> Self {
846 Self(nucleo::Config::DEFAULT)
847 }
848}
849
850#[derive(Debug, Clone, Serialize, Deserialize)]
851#[serde(default)]
852#[derive(Default)]
853struct MatcherConfigHelper {
854 pub normalize: Option<bool>,
855 pub ignore_case: Option<bool>,
856 pub prefer_prefix: Option<bool>,
857}
858
859
860impl serde::Serialize for NucleoMatcherConfig {
861 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
862 where
863 S: serde::Serializer,
864 {
865 let helper = MatcherConfigHelper {
866 normalize: Some(self.0.normalize),
867 ignore_case: Some(self.0.ignore_case),
868 prefer_prefix: Some(self.0.prefer_prefix),
869 };
870 helper.serialize(serializer)
871 }
872}
873
874impl<'de> Deserialize<'de> for NucleoMatcherConfig {
875 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
876 where
877 D: serde::Deserializer<'de>, {
878 let helper = MatcherConfigHelper::deserialize(deserializer)?;
879 let mut config = nucleo::Config::DEFAULT;
880
881 if let Some(norm) = helper.normalize {
882 config.normalize = norm;
883 }
884 if let Some(ic) = helper.ignore_case {
885 config.ignore_case = ic;
886 }
887 if let Some(pp) = helper.prefer_prefix {
888 config.prefer_prefix = pp;
889 }
890
891 Ok(NucleoMatcherConfig(config))
892 }
893}
894
895impl serde::Serialize for Split {
896 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
897 where
898 S: serde::Serializer,
899 {
900 match self {
901 Split::Delimiter(r) => serializer.serialize_str(r.as_str()),
902 Split::Regexes(rs) => {
903 let mut seq = serializer.serialize_seq(Some(rs.len()))?;
904 for r in rs {
905 seq.serialize_element(r.as_str())?;
906 }
907 seq.end()
908 }
909 Split::None => serializer.serialize_none(),
910 }
911 }
912}
913
914impl<'de> Deserialize<'de> for Split {
915 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
916 where
917 D: Deserializer<'de>, {
918 struct SplitVisitor;
919
920 impl<'de> Visitor<'de> for SplitVisitor {
921 type Value = Split;
922
923 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
924 formatter.write_str("string for delimiter or array of strings for regexes")
925 }
926
927 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
928 where
929 E: de::Error,
930 {
931 Regex::new(value)
933 .map(Split::Delimiter)
934 .map_err(|e| E::custom(format!("Invalid regex: {}", e)))
935 }
936
937 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
938 where
939 A: serde::de::SeqAccess<'de>,
940 {
941 let mut regexes = Vec::new();
942 while let Some(s) = seq.next_element::<String>()? {
943 let r = Regex::new(&s).map_err(|e| de::Error::custom(format!("Invalid regex: {}", e)))?;
944 regexes.push(r);
945 }
946 Ok(Split::Regexes(regexes))
947 }
948 }
949
950 deserializer.deserialize_any(SplitVisitor)
951 }
952}
953
954
955impl serde::Serialize for ColumnSetting {
956 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
957 where
958 S: serde::Serializer,
959 {
960 use serde::ser::SerializeStruct;
961 let mut state = serializer.serialize_struct("ColumnSetting", 3)?;
962 state.serialize_field("filter", &self.filter)?;
963 state.serialize_field("hidden", &self.hidden)?;
964 state.serialize_field("name", &self.name)?;
965 state.end()
966 }
967}
968
969impl<'de> Deserialize<'de> for ColumnSetting {
970 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
971 where
972 D: Deserializer<'de>, {
973 #[derive(Deserialize)]
974 #[serde(deny_unknown_fields)]
975 struct ColumnStruct {
976 #[serde(default = "default_true")]
977 filter: bool,
978 #[serde(default)]
979 hidden: bool,
980 name: String,
981 }
982
983 fn default_true() -> bool { true }
984
985 #[derive(Deserialize)]
986 #[serde(untagged)]
987 enum Input {
988 Str(String),
989 Obj(ColumnStruct),
990 }
991
992 match Input::deserialize(deserializer)? {
993 Input::Str(name) => Ok(ColumnSetting {
994 filter: true,
995 hidden: false,
996 name,
997 }),
998 Input::Obj(obj) => Ok(ColumnSetting {
999 filter: obj.filter,
1000 hidden: obj.hidden,
1001 name: obj.name,
1002 }),
1003 }
1004 }
1005}
1006
1007pub fn serialize_padding<S>(padding: &Padding, serializer: S) -> Result<S::Ok, S::Error>
1008where
1009S: serde::Serializer,
1010{
1011 use serde::ser::SerializeSeq;
1012 if padding.top == padding.bottom && padding.left == padding.right && padding.top == padding.left {
1013 serializer.serialize_u16(padding.top)
1014 } else if padding.top == padding.bottom && padding.left == padding.right {
1015 let mut seq = serializer.serialize_seq(Some(2))?;
1016 seq.serialize_element(&padding.left)?;
1017 seq.serialize_element(&padding.top)?;
1018 seq.end()
1019 } else {
1020 let mut seq = serializer.serialize_seq(Some(4))?;
1021 seq.serialize_element(&padding.top)?;
1022 seq.serialize_element(&padding.right)?;
1023 seq.serialize_element(&padding.bottom)?;
1024 seq.serialize_element(&padding.left)?;
1025 seq.end()
1026 }
1027}
1028
1029pub fn deserialize_padding<'de, D>(deserializer: D) -> Result<Padding, D::Error>
1030where
1031D: Deserializer<'de>,
1032{
1033 struct PaddingVisitor;
1034
1035 impl<'de> Visitor<'de> for PaddingVisitor {
1036 type Value = Padding;
1037
1038 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1039 formatter.write_str("a number or an array of 1, 2, or 4 numbers")
1040 }
1041
1042 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
1043 where
1044 E: de::Error,
1045 {
1046 let v = u16::try_from(value)
1047 .map_err(|_| E::custom(format!("padding value {} is out of range for u16", value)))?;
1048
1049 Ok(Padding {
1050 top: v,
1051 right: v,
1052 bottom: v,
1053 left: v,
1054 })
1055 }
1056
1057 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
1058 where
1059 E: de::Error,
1060 {
1061 let v = u16::try_from(value)
1062 .map_err(|_| E::custom(format!("padding value {} is out of range for u16", value)))?;
1063
1064 Ok(Padding {
1065 top: v,
1066 right: v,
1067 bottom: v,
1068 left: v,
1069 })
1070 }
1071
1072 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
1074 where
1075 A: de::SeqAccess<'de>,
1076 {
1077 let first: u16 = seq
1078 .next_element()?
1079 .ok_or_else(|| de::Error::invalid_length(0, &self))?;
1080
1081 let second: Option<u16> = seq.next_element()?;
1082 let third: Option<u16> = seq.next_element()?;
1083 let fourth: Option<u16> = seq.next_element()?;
1084
1085 match (second, third, fourth) {
1086 (None, None, None) => Ok(Padding {
1087 top: first,
1088 right: first,
1089 bottom: first,
1090 left: first,
1091 }),
1092 (Some(v2), None, None) => Ok(Padding {
1093 top: first,
1094 bottom: first,
1095 left: v2,
1096 right: v2,
1097 }),
1098 (Some(v2), Some(v3), Some(v4)) => Ok(Padding {
1099 top: first,
1100 right: v2,
1101 bottom: v3,
1102 left: v4,
1103 }),
1104 _ => Err(de::Error::invalid_length(3, &self)),
1105 }
1106 }
1107 }
1108
1109 deserializer.deserialize_any(PaddingVisitor)
1110}
1111
1112pub fn deserialize_option_auto<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
1113where
1114D: serde::Deserializer<'de>,
1115T: Deserialize<'de>,
1116{
1117 let opt = Option::<String>::deserialize(deserializer)?;
1118 match opt.as_deref() {
1119 Some("auto") => Ok(None),
1120 Some(s) => Ok(Some(T::deserialize(s.into_deserializer())?)),
1121 None => Ok(None),
1122 }
1123}
1124
1125
1126
1127impl<'de> Deserialize<'de> for BorderSetting {
1128 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1129 where
1130 D: Deserializer<'de>,
1131 {
1132 #[derive(Deserialize)]
1133 struct Helper {
1134 #[serde(default)]
1135 r#type: Option<String>,
1136 #[serde(default)]
1137 color: Option<Color>,
1138 #[serde(default, deserialize_with = "deserialize_borders_option")]
1139 sides: Option<Borders>, #[serde(default, deserialize_with = "deserialize_padding")]
1141 padding: Padding,
1142 #[serde(default)]
1143 title: String,
1144 #[serde(default, deserialize_with = "deserialize_modifier", serialize_with = "serialize_modifier")]
1145 title_modifier: Modifier,
1146 #[serde(default)]
1147 bg: Color,
1148 }
1149
1150 let h = Helper::deserialize(deserializer)?;
1151
1152 let r#type = match h.r#type {
1154 Some(ref s) => s.parse::<BorderType>().map_err(serde::de::Error::custom)?,
1155 None => BorderType::default(),
1156 };
1157
1158 let sides = if let Some(sides) = h.sides {
1160 sides
1161 } else if h.r#type.is_some() || h.color.is_some() {
1162 Borders::ALL
1163 } else {
1164 Borders::NONE
1165 };
1166
1167 Ok(BorderSetting {
1168 r#type,
1169 color: h.color.unwrap_or_default(),
1170 sides,
1171 padding: h.padding,
1172 title: h.title,
1173 title_modifier: h.title_modifier,
1174 bg: h.bg,
1175 })
1176 }
1177}
1178
1179fn default_true() -> bool {
1182 true
1183}
1184
1185fn default_black() -> Color {
1186 Color::Black
1187}
1188
1189fn default_green() -> Color {
1190 Color::Green
1191}
1192
1193fn default_bold() -> Modifier {
1194 Modifier::BOLD
1195}
1196
1197fn default_italic() -> Modifier {
1198 Modifier::ITALIC
1199}