Skip to main content

matchmaker/
config_types.rs

1use std::fmt;
2
3use cli_boilerplate_automation::{
4    bird::one_or_many, define_restricted_wrapper, define_transparent_wrapper,
5};
6use ratatui::widgets::Borders;
7
8use regex::Regex;
9
10use serde::{
11    Deserialize, Deserializer, Serialize, Serializer,
12    de::{self, Visitor},
13    ser::SerializeSeq,
14};
15
16#[derive(Clone, Copy, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)]
17pub enum HorizontalSeparator {
18    #[default]
19    None,
20    Empty,
21    Light,
22    Normal,
23    Heavy,
24    Dashed,
25}
26
27impl HorizontalSeparator {
28    pub fn as_str(self) -> &'static str {
29        match self {
30            Self::None => unreachable!(),
31            Self::Empty => " ",
32            Self::Light => "─", // U+2500
33            Self::Normal => "─",
34            Self::Heavy => "━",  // U+2501
35            Self::Dashed => "╌", // U+254C (box drawings light double dash)
36        }
37    }
38}
39
40#[derive(Debug, Default, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
41pub enum RowConnectionStyle {
42    #[default]
43    Disjoint,
44    Capped,
45    Full,
46}
47
48define_transparent_wrapper!(
49    #[derive(Copy, Clone, serde::Serialize, serde::Deserialize)]
50    #[serde(transparent)]
51    Count: u16 = 1
52);
53use ratatui::widgets::Padding as rPadding;
54
55define_transparent_wrapper!(
56    #[derive(Copy, Clone, Default)]
57    Padding: rPadding
58);
59
60#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
61#[serde(untagged)]
62pub enum ShowCondition {
63    Bool(bool),
64    Free(u16),
65}
66impl Default for ShowCondition {
67    fn default() -> Self {
68        Self::Bool(false)
69    }
70}
71impl From<bool> for ShowCondition {
72    fn from(value: bool) -> Self {
73        ShowCondition::Bool(value)
74    }
75}
76
77impl From<u16> for ShowCondition {
78    fn from(value: u16) -> Self {
79        ShowCondition::Free(value)
80    }
81}
82
83// -----------------------------------------------------------------------------------------
84
85impl Serialize for Padding {
86    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
87    where
88        S: Serializer,
89    {
90        use serde::ser::SerializeSeq;
91        let padding = self;
92        if padding.top == padding.bottom
93            && padding.left == padding.right
94            && padding.top == padding.left
95        {
96            serializer.serialize_u16(padding.top)
97        } else if padding.top == padding.bottom && padding.left == padding.right {
98            let mut seq = serializer.serialize_seq(Some(2))?;
99            seq.serialize_element(&padding.left)?;
100            seq.serialize_element(&padding.top)?;
101            seq.end()
102        } else {
103            let mut seq = serializer.serialize_seq(Some(4))?;
104            seq.serialize_element(&padding.top)?;
105            seq.serialize_element(&padding.right)?;
106            seq.serialize_element(&padding.bottom)?;
107            seq.serialize_element(&padding.left)?;
108            seq.end()
109        }
110    }
111}
112
113impl<'de> Deserialize<'de> for Padding {
114    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
115    where
116        D: Deserializer<'de>,
117    {
118        use serde::de::Error;
119
120        let repr: Vec<u16> = one_or_many::deserialize(deserializer)?;
121
122        let inner = match repr.len() {
123            1 => {
124                let v = repr[0];
125                rPadding {
126                    top: v,
127                    right: v,
128                    bottom: v,
129                    left: v,
130                }
131            }
132            2 => {
133                let lr = repr[0];
134                let tb = repr[1];
135                rPadding {
136                    top: tb,
137                    right: lr,
138                    bottom: tb,
139                    left: lr,
140                }
141            }
142            4 => rPadding {
143                top: repr[0],
144                right: repr[1],
145                bottom: repr[2],
146                left: repr[3],
147            },
148            _ => {
149                return Err(D::Error::custom(
150                    "a number or an array of 1, 2, or 4 numbers",
151                ));
152            }
153        };
154
155        Ok(inner.into())
156    }
157}
158
159// ---------------------------------------------------------------------------------
160
161define_restricted_wrapper!(
162    #[derive(Clone, serde::Serialize, serde::Deserialize)]
163    #[serde(transparent)]
164    FormatString: String
165);
166
167#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
168#[serde(rename_all = "lowercase")]
169pub enum Side {
170    Top,
171    Bottom,
172    Left,
173    #[default]
174    Right,
175}
176
177impl Side {
178    pub fn opposite(&self) -> Borders {
179        match self {
180            Side::Top => Borders::BOTTOM,
181            Side::Bottom => Borders::TOP,
182            Side::Left => Borders::RIGHT,
183            Side::Right => Borders::LEFT,
184        }
185    }
186}
187
188#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
189#[serde(rename_all = "lowercase")]
190pub enum CursorSetting {
191    None,
192    #[default]
193    Default,
194}
195
196#[derive(Default, Debug, Clone, PartialEq, serde::Serialize)]
197pub struct ColumnSetting {
198    pub filter: bool,
199    pub hidden: bool,
200    pub name: String,
201}
202
203#[derive(Default, Debug, Clone)]
204pub enum Split {
205    /// Split by delimiter. Supports regex.
206    Delimiter(Regex),
207    /// A sequence of regexes.
208    Regexes(Vec<Regex>),
209    /// No splitting.
210    #[default]
211    None,
212}
213
214impl PartialEq for Split {
215    fn eq(&self, other: &Self) -> bool {
216        match (self, other) {
217            (Split::Delimiter(r1), Split::Delimiter(r2)) => r1.as_str() == r2.as_str(),
218            (Split::Regexes(v1), Split::Regexes(v2)) => {
219                if v1.len() != v2.len() {
220                    return false;
221                }
222                v1.iter()
223                    .zip(v2.iter())
224                    .all(|(r1, r2)| r1.as_str() == r2.as_str())
225            }
226            (Split::None, Split::None) => true,
227            _ => false,
228        }
229    }
230}
231
232// ---------------------------------------------------------------------------------
233
234impl serde::Serialize for Split {
235    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
236    where
237        S: serde::Serializer,
238    {
239        match self {
240            Split::Delimiter(r) => serializer.serialize_str(r.as_str()),
241            Split::Regexes(rs) => {
242                let mut seq = serializer.serialize_seq(Some(rs.len()))?;
243                for r in rs {
244                    seq.serialize_element(r.as_str())?;
245                }
246                seq.end()
247            }
248            Split::None => serializer.serialize_none(),
249        }
250    }
251}
252
253impl<'de> Deserialize<'de> for Split {
254    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
255    where
256        D: Deserializer<'de>,
257    {
258        struct SplitVisitor;
259
260        impl<'de> Visitor<'de> for SplitVisitor {
261            type Value = Split;
262
263            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
264                formatter.write_str("string for delimiter or array of strings for regexes")
265            }
266
267            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
268            where
269                E: de::Error,
270            {
271                // Try to compile single regex
272                Regex::new(value)
273                    .map(Split::Delimiter)
274                    .map_err(|e| E::custom(format!("Invalid regex: {}", e)))
275            }
276
277            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
278            where
279                A: serde::de::SeqAccess<'de>,
280            {
281                let mut regexes = Vec::new();
282                while let Some(s) = seq.next_element::<String>()? {
283                    let r = Regex::new(&s)
284                        .map_err(|e| de::Error::custom(format!("Invalid regex: {}", e)))?;
285                    regexes.push(r);
286                }
287                Ok(Split::Regexes(regexes))
288            }
289        }
290
291        deserializer.deserialize_any(SplitVisitor)
292    }
293}
294
295impl<'de> Deserialize<'de> for ColumnSetting {
296    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
297    where
298        D: Deserializer<'de>,
299    {
300        #[derive(Deserialize)]
301        #[serde(deny_unknown_fields)]
302        struct ColumnStruct {
303            #[serde(default = "default_true")]
304            filter: bool,
305            #[serde(default)]
306            hidden: bool,
307            name: String,
308        }
309
310        fn default_true() -> bool {
311            true
312        }
313
314        #[derive(Deserialize)]
315        #[serde(untagged)]
316        enum Input {
317            Str(String),
318            Obj(ColumnStruct),
319        }
320
321        match Input::deserialize(deserializer)? {
322            Input::Str(name) => Ok(ColumnSetting {
323                filter: true,
324                hidden: false,
325                name,
326            }),
327            Input::Obj(obj) => Ok(ColumnSetting {
328                filter: obj.filter,
329                hidden: obj.hidden,
330                name: obj.name,
331            }),
332        }
333    }
334}
335
336// ----------------------------------------------------------------------
337pub fn deserialize_string_or_char_as_double_width<'de, D, T>(deserializer: D) -> Result<T, D::Error>
338where
339    D: Deserializer<'de>,
340    T: From<String>,
341{
342    struct GenericVisitor<T> {
343        _marker: std::marker::PhantomData<T>,
344    }
345
346    impl<'de, T> Visitor<'de> for GenericVisitor<T>
347    where
348        T: From<String>,
349    {
350        type Value = T;
351
352        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
353            formatter.write_str("a string or single character")
354        }
355
356        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
357        where
358            E: de::Error,
359        {
360            let s = if v.chars().count() == 1 {
361                let mut s = String::with_capacity(2);
362                s.push(v.chars().next().unwrap());
363                s.push(' ');
364                s
365            } else {
366                v.to_string()
367            };
368            Ok(T::from(s))
369        }
370
371        fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
372        where
373            E: de::Error,
374        {
375            self.visit_str(&v)
376        }
377    }
378
379    deserializer.deserialize_string(GenericVisitor {
380        _marker: std::marker::PhantomData,
381    })
382}