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
194#[derive(Debug, Clone, PartialEq, Serialize, Default)]
195#[serde(transparent)]
196pub struct ColumnName(String);
197
198impl<'de> Deserialize<'de> for ColumnName {
199 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
200 where
201 D: Deserializer<'de>,
202 {
203 let s = String::deserialize(deserializer)?;
204 if s.chars().all(|c| c.is_alphanumeric()) {
205 Ok(ColumnName(s))
206 } else {
207 Err(serde::de::Error::custom(format!(
208 "Invalid column name '{}': name must be alphanumeric",
209 s
210 )))
211 }
212 }
213}
214
215impl ColumnName {
216 pub fn as_str(&self) -> &str {
217 &self.0
218 }
219}
220
221impl PartialEq<&str> for ColumnName {
222 fn eq(&self, other: &&str) -> bool {
223 self.0 == *other
224 }
225}
226
227impl From<String> for ColumnName {
228 fn from(s: String) -> Self {
229 ColumnName(s)
230 }
231}
232
233impl AsRef<str> for ColumnName {
234 fn as_ref(&self) -> &str {
235 &self.0
236 }
237}
238
239impl fmt::Display for ColumnName {
240 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241 write!(f, "{}", self.0)
242 }
243}
244
245#[derive(Default, Debug, Clone, PartialEq, serde::Serialize)]
246pub struct ColumnSetting {
247 pub filter: bool,
248 pub hidden: bool,
249 pub name: ColumnName,
250}
251
252#[derive(Default, Debug, Clone)]
253pub enum Split {
254 Delimiter(Regex),
256 Regexes(Vec<Regex>),
258 #[default]
260 None,
261}
262
263impl PartialEq for Split {
264 fn eq(&self, other: &Self) -> bool {
265 match (self, other) {
266 (Split::Delimiter(r1), Split::Delimiter(r2)) => r1.as_str() == r2.as_str(),
267 (Split::Regexes(v1), Split::Regexes(v2)) => {
268 if v1.len() != v2.len() {
269 return false;
270 }
271 v1.iter()
272 .zip(v2.iter())
273 .all(|(r1, r2)| r1.as_str() == r2.as_str())
274 }
275 (Split::None, Split::None) => true,
276 _ => false,
277 }
278 }
279}
280
281impl serde::Serialize for Split {
284 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
285 where
286 S: serde::Serializer,
287 {
288 match self {
289 Split::Delimiter(r) => serializer.serialize_str(r.as_str()),
290 Split::Regexes(rs) => {
291 let mut seq = serializer.serialize_seq(Some(rs.len()))?;
292 for r in rs {
293 seq.serialize_element(r.as_str())?;
294 }
295 seq.end()
296 }
297 Split::None => serializer.serialize_none(),
298 }
299 }
300}
301
302impl<'de> Deserialize<'de> for Split {
303 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
304 where
305 D: Deserializer<'de>,
306 {
307 struct SplitVisitor;
308
309 impl<'de> Visitor<'de> for SplitVisitor {
310 type Value = Split;
311
312 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
313 formatter.write_str("string for delimiter or array of strings for regexes")
314 }
315
316 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
317 where
318 E: de::Error,
319 {
320 Regex::new(value)
322 .map(Split::Delimiter)
323 .map_err(|e| E::custom(format!("Invalid regex: {}", e)))
324 }
325
326 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
327 where
328 A: serde::de::SeqAccess<'de>,
329 {
330 let mut regexes = Vec::new();
331 while let Some(s) = seq.next_element::<String>()? {
332 let r = Regex::new(&s)
333 .map_err(|e| de::Error::custom(format!("Invalid regex: {}", e)))?;
334 regexes.push(r);
335 }
336 Ok(Split::Regexes(regexes))
337 }
338 }
339
340 deserializer.deserialize_any(SplitVisitor)
341 }
342}
343
344impl<'de> Deserialize<'de> for ColumnSetting {
345 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
346 where
347 D: Deserializer<'de>,
348 {
349 #[derive(Deserialize)]
350 #[serde(deny_unknown_fields)]
351 struct ColumnStruct {
352 #[serde(default = "default_true")]
353 filter: bool,
354 #[serde(default)]
355 hidden: bool,
356 name: ColumnName,
357 }
358
359 fn default_true() -> bool {
360 true
361 }
362
363 #[derive(Deserialize)]
364 #[serde(untagged)]
365 enum Input {
366 Str(ColumnName),
367 Obj(ColumnStruct),
368 }
369
370 match Input::deserialize(deserializer)? {
371 Input::Str(name) => Ok(ColumnSetting {
372 filter: true,
373 hidden: false,
374 name,
375 }),
376 Input::Obj(obj) => Ok(ColumnSetting {
377 filter: obj.filter,
378 hidden: obj.hidden,
379 name: obj.name,
380 }),
381 }
382 }
383}
384
385pub fn deserialize_string_or_char_as_double_width<'de, D, T>(deserializer: D) -> Result<T, D::Error>
387where
388 D: Deserializer<'de>,
389 T: From<String>,
390{
391 struct GenericVisitor<T> {
392 _marker: std::marker::PhantomData<T>,
393 }
394
395 impl<'de, T> Visitor<'de> for GenericVisitor<T>
396 where
397 T: From<String>,
398 {
399 type Value = T;
400
401 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
402 formatter.write_str("a string or single character")
403 }
404
405 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
406 where
407 E: de::Error,
408 {
409 let s = if v.chars().count() == 1 {
410 let mut s = String::with_capacity(2);
411 s.push(v.chars().next().unwrap());
412 s.push(' ');
413 s
414 } else {
415 v.to_string()
416 };
417 Ok(T::from(s))
418 }
419
420 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
421 where
422 E: de::Error,
423 {
424 self.visit_str(&v)
425 }
426 }
427
428 deserializer.deserialize_string(GenericVisitor {
429 _marker: std::marker::PhantomData,
430 })
431}
432
433define_transparent_wrapper!(
435 #[derive(Clone, Eq, Serialize)]
436 StringValue: String
437
438);
439
440impl<'de> Deserialize<'de> for StringValue {
441 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
442 where
443 D: Deserializer<'de>,
444 {
445 struct Visitor;
446
447 impl<'de> de::Visitor<'de> for Visitor {
448 type Value = StringValue;
449
450 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
451 formatter.write_str("a string, number, or bool")
452 }
453
454 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> {
455 Ok(StringValue(v.to_owned()))
456 }
457
458 fn visit_string<E>(self, v: String) -> Result<Self::Value, E> {
459 Ok(StringValue(v))
460 }
461
462 fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E> {
463 Ok(StringValue(v.to_string()))
464 }
465
466 fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> {
467 Ok(StringValue(v.to_string()))
468 }
469
470 fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E> {
471 Ok(StringValue(v.to_string()))
472 }
473
474 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E> {
475 Ok(StringValue(v.to_string()))
476 }
477 }
478
479 deserializer.deserialize_any(Visitor)
480 }
481}
482
483#[cfg(test)]
484mod tests {
485 use super::*;
486
487 #[derive(Deserialize)]
488 struct TestName {
489 name: ColumnName,
490 }
491
492 #[test]
493 fn test_column_name_validation() {
494 let name: TestName = toml::from_str("name = \"col1\"").expect("Valid name");
496 assert_eq!(name.name.as_ref(), "col1");
497
498 let name: TestName = toml::from_str("name = \"Column123\"").expect("Valid name");
499 assert_eq!(name.name.as_ref(), "Column123");
500
501 let res: Result<TestName, _> = toml::from_str("name = \"col-1\"");
503 assert!(res.is_err());
504
505 let res: Result<TestName, _> = toml::from_str("name = \"col 1\"");
506 assert!(res.is_err());
507
508 let res: Result<TestName, _> = toml::from_str("name = \"col_1\"");
509 assert!(res.is_err());
510 }
511
512 #[derive(Deserialize)]
513 struct TestSetting {
514 setting: ColumnSetting,
515 }
516
517 #[test]
518 fn test_column_setting_deserialization() {
519 let setting: TestSetting = toml::from_str("setting = \"col1\"").expect("Valid name");
521 assert_eq!(setting.setting.name.as_ref(), "col1");
522 assert!(setting.setting.filter);
523 assert!(!setting.setting.hidden);
524
525 let setting: TestSetting = toml::from_str("setting = { name = \"col2\", filter = false }").expect("Valid object");
527 assert_eq!(setting.setting.name.as_ref(), "col2");
528 assert!(!setting.setting.filter);
529
530 let res: Result<TestSetting, _> = toml::from_str("setting = { name = \"col-2\" }");
532 assert!(res.is_err());
533 }
534}