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 => "─", Self::Normal => "─",
34 Self::Heavy => "━", Self::Dashed => "╌", }
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
83impl 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
159define_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 Delimiter(Regex),
207 Regexes(Vec<Regex>),
209 #[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
232impl 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 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
336pub 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}