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