1use std::fmt;
2
3use cba::{bird::one_or_many, define_transparent_wrapper};
4use ratatui::widgets::Borders;
5
6use regex::Regex;
7
8use serde::{
9 Deserialize, Deserializer, Serialize, Serializer,
10 de::{self, Visitor},
11 ser::SerializeSeq,
12};
13
14#[derive(Clone, Copy, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)]
15pub enum HorizontalSeparator {
16 #[default]
17 None,
18 Empty,
19 Light,
20 Normal,
21 Heavy,
22 Dashed,
23}
24
25impl HorizontalSeparator {
26 pub fn as_str(self) -> &'static str {
27 match self {
28 Self::None => unreachable!(),
29 Self::Empty => " ",
30 Self::Light => "─", Self::Normal => "─",
32 Self::Heavy => "━", Self::Dashed => "╌", }
35 }
36}
37
38#[derive(Debug, Default, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
39pub enum RowConnectionStyle {
40 #[default]
41 Disjoint,
42 Capped,
43 Full,
44}
45
46define_transparent_wrapper!(
47 #[derive(Copy, Clone, serde::Serialize, serde::Deserialize)]
48 #[serde(transparent)]
49 Count: u16 = 1
50);
51use ratatui::widgets::Padding as rPadding;
52
53define_transparent_wrapper!(
54 #[derive(Copy, Clone, Default)]
55 Padding: rPadding
56);
57
58#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
59#[serde(untagged)]
60pub enum ShowCondition {
61 Bool(bool),
62 Free(u16),
63}
64impl Default for ShowCondition {
65 fn default() -> Self {
66 Self::Bool(false)
67 }
68}
69impl From<bool> for ShowCondition {
70 fn from(value: bool) -> Self {
71 ShowCondition::Bool(value)
72 }
73}
74
75impl From<u16> for ShowCondition {
76 fn from(value: u16) -> Self {
77 ShowCondition::Free(value)
78 }
79}
80
81impl Serialize for Padding {
84 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
85 where
86 S: Serializer,
87 {
88 use serde::ser::SerializeSeq;
89 let padding = self;
90 if padding.top == padding.bottom
91 && padding.left == padding.right
92 && padding.top == padding.left
93 {
94 serializer.serialize_u16(padding.top)
95 } else if padding.top == padding.bottom && padding.left == padding.right {
96 let mut seq = serializer.serialize_seq(Some(2))?;
97 seq.serialize_element(&padding.left)?;
98 seq.serialize_element(&padding.top)?;
99 seq.end()
100 } else {
101 let mut seq = serializer.serialize_seq(Some(4))?;
102 seq.serialize_element(&padding.top)?;
103 seq.serialize_element(&padding.right)?;
104 seq.serialize_element(&padding.bottom)?;
105 seq.serialize_element(&padding.left)?;
106 seq.end()
107 }
108 }
109}
110
111impl<'de> Deserialize<'de> for Padding {
112 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
113 where
114 D: Deserializer<'de>,
115 {
116 use serde::de::Error;
117
118 let repr: Vec<u16> = one_or_many::deserialize(deserializer)?;
119
120 let inner = match repr.len() {
121 1 => {
122 let v = repr[0];
123 rPadding {
124 top: v,
125 right: v,
126 bottom: v,
127 left: v,
128 }
129 }
130 2 => {
131 let lr = repr[0];
132 let tb = repr[1];
133 rPadding {
134 top: tb,
135 right: lr,
136 bottom: tb,
137 left: lr,
138 }
139 }
140 4 => rPadding {
141 top: repr[0],
142 right: repr[1],
143 bottom: repr[2],
144 left: repr[3],
145 },
146 _ => {
147 return Err(D::Error::custom(
148 "a number or an array of 1, 2, or 4 numbers",
149 ));
150 }
151 };
152
153 Ok(inner.into())
154 }
155}
156
157#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
166#[serde(rename_all = "lowercase")]
167pub enum Side {
168 Top,
169 Bottom,
170 Left,
171 #[default]
172 Right,
173}
174
175impl Side {
176 pub fn opposite(&self) -> Borders {
177 match self {
178 Side::Top => Borders::BOTTOM,
179 Side::Bottom => Borders::TOP,
180 Side::Left => Borders::RIGHT,
181 Side::Right => Borders::LEFT,
182 }
183 }
184}
185
186#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
187#[serde(rename_all = "lowercase")]
188pub enum CursorSetting {
189 None,
190 #[default]
191 Default,
192}
193
194define_transparent_wrapper!(
195 #[derive(Clone, Serialize, Default)]
196 #[serde(transparent)]
197 ColumnName: String
198);
199
200impl<'de> Deserialize<'de> for ColumnName {
201 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
202 where
203 D: Deserializer<'de>,
204 {
205 let s = String::deserialize(deserializer)?;
206 if s.chars().all(|c| c.is_alphanumeric()) {
207 Ok(ColumnName(s))
208 } else {
209 Err(serde::de::Error::custom(format!(
210 "Invalid column name '{}': name must be alphanumeric",
211 s
212 )))
213 }
214 }
215}
216
217#[derive(Default, Debug, Clone, PartialEq, serde::Serialize)]
218pub struct ColumnSetting {
219 pub filter: bool,
220 pub hidden: bool,
221 pub name: ColumnName,
222}
223
224#[derive(Default, Debug, Clone)]
225pub enum Split {
226 Delimiter(Regex),
228 Regexes(Vec<Regex>),
230 #[default]
232 None,
233}
234
235impl PartialEq for Split {
236 fn eq(&self, other: &Self) -> bool {
237 match (self, other) {
238 (Split::Delimiter(r1), Split::Delimiter(r2)) => r1.as_str() == r2.as_str(),
239 (Split::Regexes(v1), Split::Regexes(v2)) => {
240 if v1.len() != v2.len() {
241 return false;
242 }
243 v1.iter()
244 .zip(v2.iter())
245 .all(|(r1, r2)| r1.as_str() == r2.as_str())
246 }
247 (Split::None, Split::None) => true,
248 _ => false,
249 }
250 }
251}
252
253impl serde::Serialize for Split {
256 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
257 where
258 S: serde::Serializer,
259 {
260 match self {
261 Split::Delimiter(r) => serializer.serialize_str(r.as_str()),
262 Split::Regexes(rs) => {
263 let mut seq = serializer.serialize_seq(Some(rs.len()))?;
264 for r in rs {
265 seq.serialize_element(r.as_str())?;
266 }
267 seq.end()
268 }
269 Split::None => serializer.serialize_none(),
270 }
271 }
272}
273
274impl<'de> Deserialize<'de> for Split {
275 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
276 where
277 D: Deserializer<'de>,
278 {
279 struct SplitVisitor;
280
281 impl<'de> Visitor<'de> for SplitVisitor {
282 type Value = Split;
283
284 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
285 formatter.write_str("string for delimiter or array of strings for regexes")
286 }
287
288 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
289 where
290 E: de::Error,
291 {
292 Regex::new(value)
294 .map(Split::Delimiter)
295 .map_err(|e| E::custom(format!("Invalid regex: {}", e)))
296 }
297
298 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
299 where
300 A: serde::de::SeqAccess<'de>,
301 {
302 let mut regexes = Vec::new();
303 while let Some(s) = seq.next_element::<String>()? {
304 let r = Regex::new(&s)
305 .map_err(|e| de::Error::custom(format!("Invalid regex: {}", e)))?;
306 regexes.push(r);
307 }
308 Ok(Split::Regexes(regexes))
309 }
310 }
311
312 deserializer.deserialize_any(SplitVisitor)
313 }
314}
315
316impl<'de> Deserialize<'de> for ColumnSetting {
317 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
318 where
319 D: Deserializer<'de>,
320 {
321 #[derive(Deserialize)]
322 #[serde(deny_unknown_fields)]
323 struct ColumnStruct {
324 #[serde(default = "default_true")]
325 filter: bool,
326 #[serde(default)]
327 hidden: bool,
328 name: ColumnName,
329 }
330
331 fn default_true() -> bool {
332 true
333 }
334
335 #[derive(Deserialize)]
336 #[serde(untagged)]
337 enum Input {
338 Str(ColumnName),
339 Obj(ColumnStruct),
340 }
341
342 match Input::deserialize(deserializer)? {
343 Input::Str(name) => Ok(ColumnSetting {
344 filter: true,
345 hidden: false,
346 name,
347 }),
348 Input::Obj(obj) => Ok(ColumnSetting {
349 filter: obj.filter,
350 hidden: obj.hidden,
351 name: obj.name,
352 }),
353 }
354 }
355}
356
357pub fn deserialize_string_or_char_as_double_width<'de, D, T>(deserializer: D) -> Result<T, D::Error>
359where
360 D: Deserializer<'de>,
361 T: From<String>,
362{
363 struct GenericVisitor<T> {
364 _marker: std::marker::PhantomData<T>,
365 }
366
367 impl<'de, T> Visitor<'de> for GenericVisitor<T>
368 where
369 T: From<String>,
370 {
371 type Value = T;
372
373 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
374 formatter.write_str("a string or single character")
375 }
376
377 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
378 where
379 E: de::Error,
380 {
381 let s = if v.chars().count() == 1 {
382 let mut s = String::with_capacity(2);
383 s.push(v.chars().next().unwrap());
384 s.push(' ');
385 s
386 } else {
387 v.to_string()
388 };
389 Ok(T::from(s))
390 }
391
392 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
393 where
394 E: de::Error,
395 {
396 self.visit_str(&v)
397 }
398 }
399
400 deserializer.deserialize_string(GenericVisitor {
401 _marker: std::marker::PhantomData,
402 })
403}
404
405define_transparent_wrapper!(
407 #[derive(Clone, Eq, Serialize)]
408 StringValue: String
409
410);
411
412impl<'de> Deserialize<'de> for StringValue {
413 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
414 where
415 D: Deserializer<'de>,
416 {
417 struct Visitor;
418
419 impl<'de> de::Visitor<'de> for Visitor {
420 type Value = StringValue;
421
422 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
423 formatter.write_str("a string, number, or bool")
424 }
425
426 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> {
427 Ok(StringValue(v.to_owned()))
428 }
429
430 fn visit_string<E>(self, v: String) -> Result<Self::Value, E> {
431 Ok(StringValue(v))
432 }
433
434 fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E> {
435 Ok(StringValue(v.to_string()))
436 }
437
438 fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> {
439 Ok(StringValue(v.to_string()))
440 }
441
442 fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E> {
443 Ok(StringValue(v.to_string()))
444 }
445
446 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E> {
447 Ok(StringValue(v.to_string()))
448 }
449 }
450
451 deserializer.deserialize_any(Visitor)
452 }
453}
454
455#[cfg(test)]
456mod tests {
457 use super::*;
458
459 #[derive(Deserialize)]
460 struct TestName {
461 name: ColumnName,
462 }
463
464 #[test]
465 fn test_column_name_validation() {
466 let name: TestName = toml::from_str("name = \"col1\"").expect("Valid name");
468 assert_eq!(name.name.as_str(), "col1");
469
470 let name: TestName = toml::from_str("name = \"Column123\"").expect("Valid name");
471 assert_eq!(name.name.as_str(), "Column123");
472
473 let res: Result<TestName, _> = toml::from_str("name = \"col-1\"");
475 assert!(res.is_err());
476
477 let res: Result<TestName, _> = toml::from_str("name = \"col 1\"");
478 assert!(res.is_err());
479
480 let res: Result<TestName, _> = toml::from_str("name = \"col_1\"");
481 assert!(res.is_err());
482 }
483}