1use std::{path::PathBuf, process};
2
3use chrono;
4
5use crate::lib::{
6 job::{self},
7 logic::{self},
8};
9pub enum ValidTablePart {
14 MD,
15 MF,
16 XD,
17 XF,
18}
19
20#[derive(Debug, Clone)]
21pub struct ValidTablePartConfig {
22 pub md: bool,
23 pub mf: bool,
24 pub xd: bool,
25 pub xf: bool,
26}
27
28impl ValidTablePart {
29 pub fn parse(s: &str) -> Result<Self, String> {
30 match s.trim().to_lowercase().as_str() {
31 "MD" | "md" | "match-dirs" => Ok(ValidTablePart::MD),
32 "MF" | "mf" | "match-files" => Ok(ValidTablePart::MF),
33 "XD" | "xd" | "mismatch-dirs" => Ok(ValidTablePart::XD),
34 "XF" | "xf" | "mismatch-files" => Ok(ValidTablePart::XF),
35 _ => Err(format!("Nieprawidłowa nazwa: '{}'. Dostępne: md, mf, xd, xf", s.trim())),
36 }
37 }
38
39 pub fn parses<'a, I>(inputs: I) -> Result<Vec<Self>, String>
40 where I: IntoIterator<Item = &'a str> {
41 inputs.into_iter().map(Self::parse).collect()
42 }
43
44 pub fn parse_vec<I>(inputs: I) -> Vec<Self>
45 where
46 I: IntoIterator,
47 I::Item: AsRef<str>, {
48 inputs.into_iter().map(|s| Self::parse(s.as_ref()).expect("invalid ValidTablePart input")).collect()
49 }
50 pub fn parse_vec_as_config<I>(inputs: I) -> ValidTablePartConfig
51 where
52 I: IntoIterator,
53 I::Item: AsRef<str>, {
54 let mut md = false;
55 let mut mf = false;
56 let mut xd = false;
57 let mut xf = false;
58
59 let mut has_any_valid = false;
60
61 for item in inputs {
62 if let Ok(parsed) = Self::parse(item.as_ref()) {
64 has_any_valid = true;
65 match parsed {
66 ValidTablePart::MD => md = true,
67 ValidTablePart::MF => mf = true,
68 ValidTablePart::XD => xd = true,
69 ValidTablePart::XF => xf = true,
70 }
71 }
72 }
73
74 if !has_any_valid {
76 md = true;
77 mf = true;
78 }
79
80 ValidTablePartConfig { md, mf, xd, xf }
81 }
82}
83
84pub enum ValidColumnItem {
89 ListNone,
90 ListTree,
91 ListFlat,
92 IconsNone,
93 IconsLite,
94 IconsMore,
95
96 NumPrefix,
97 NumSuffix,
98 NameNone,
99 NameShow,
100 WhitespaceTrailNone,
101 WhitespaceTrailShow,
102}
103
104#[derive(Debug)]
105pub struct ValidColumnItemConfig {
106 pub list: ValidModeItemList,
107 pub icons: ValidModeItemIcons,
108 pub name: bool,
109 pub align_end: bool,
110 pub num_is_first: bool,
111}
112
113#[derive(Debug, PartialEq, Eq)]
114pub enum ValidModeItemList {
115 None,
116 Flat,
117 Tree,
118}
119
120#[derive(Debug, PartialEq, Eq)]
121pub enum ValidModeItemIcons {
122 Lite,
123 More,
124 None,
125}
126
127impl ValidColumnItem {
128 pub fn parse(s: &str) -> Result<Self, String> {
129 match s.trim().to_lowercase().as_str() {
130 "list-none" => Ok(ValidColumnItem::ListNone),
131 "list-flat" => Ok(ValidColumnItem::ListFlat),
132 "list-tree" => Ok(ValidColumnItem::ListTree),
133
134 "icons-lite" => Ok(ValidColumnItem::IconsLite),
135 "icons-more" => Ok(ValidColumnItem::IconsMore),
136 "icons-none" => Ok(ValidColumnItem::IconsNone),
137
138 "num-prefix" => Ok(ValidColumnItem::NumPrefix),
139 "num-suffix" => Ok(ValidColumnItem::NumSuffix),
140
141 "name-none" => Ok(ValidColumnItem::NameNone),
142 "name-show" => Ok(ValidColumnItem::NameShow),
143
144 "ws-none" | "whitespace-none" => Ok(ValidColumnItem::WhitespaceTrailNone),
145 "ws-show" | "whitespace-show" => Ok(ValidColumnItem::WhitespaceTrailShow),
146
147 _ => Err(format!("Nieznany ValidColumnItem: '{}'", s.trim())),
148 }
149 }
150
151 pub fn parse_vec<I>(inputs: I) -> Result<Vec<Self>, String>
152 where
153 I: IntoIterator,
154 I::Item: AsRef<str>, {
155 inputs.into_iter().map(|s| Self::parse(s.as_ref())).collect()
156 }
157
158 pub fn parse_vec_as_config<I>(inputs: I) -> Result<ValidColumnItemConfig, String>
159 where
160 I: IntoIterator,
161 I::Item: AsRef<str>, {
162 let items = Self::parse_vec(inputs)?;
163 Ok(ValidColumnItemConfig::get(items))
164 }
165}
166
167impl ValidColumnItemConfig {
168 pub fn get(items: Vec<ValidColumnItem>) -> Self {
169 let mut list: Option<ValidModeItemList> = None;
170 let mut icons: Option<ValidModeItemIcons> = None;
171 let mut num_is_first: Option<bool> = None;
172 let mut name: Option<bool> = None;
173 let mut align_end: Option<bool> = None;
174
175 let mut list_count = 0usize;
176 let mut icons_count = 0usize;
177 let mut num_count = 0usize;
178 let mut name_count = 0usize;
179 let mut ws_count = 0usize;
180
181 for item in items {
182 match item {
183 ValidColumnItem::ListNone => {
185 list = Some(ValidModeItemList::None);
186 list_count += 1;
187 }
188 ValidColumnItem::ListFlat => {
189 list = Some(ValidModeItemList::Flat);
190 list_count += 1;
191 }
192 ValidColumnItem::ListTree => {
193 list = Some(ValidModeItemList::Tree);
194 list_count += 1;
195 }
196
197 ValidColumnItem::NumPrefix => {
199 num_is_first = Some(true);
200 num_count += 1;
201 }
202 ValidColumnItem::NumSuffix => {
203 num_is_first = Some(false);
204 num_count += 1;
205 }
206
207 ValidColumnItem::IconsLite => {
209 icons = Some(ValidModeItemIcons::Lite);
210 icons_count += 1;
211 }
212 ValidColumnItem::IconsMore => {
213 icons = Some(ValidModeItemIcons::More);
214 icons_count += 1;
215 }
216 ValidColumnItem::IconsNone => {
217 icons = Some(ValidModeItemIcons::None);
218 icons_count += 1;
219 }
220
221 ValidColumnItem::NameNone => {
223 name = Some(false);
224 name_count += 1;
225 }
226 ValidColumnItem::NameShow => {
227 name = Some(true);
228 name_count += 1;
229 }
230
231 ValidColumnItem::WhitespaceTrailNone => {
233 align_end = Some(false);
234 ws_count += 1;
235 }
236 ValidColumnItem::WhitespaceTrailShow => {
237 align_end = Some(true);
238 ws_count += 1;
239 }
240 }
241 }
242
243 Self {
244 list: if list_count == 1 { list.unwrap() } else { ValidModeItemList::Tree },
245
246 icons: if icons_count == 1 { icons.unwrap() } else { ValidModeItemIcons::Lite },
247
248 num_is_first: if num_count == 1 { num_is_first.unwrap() } else { false },
249
250 name: if name_count == 1 { name.unwrap() } else { false },
251
252 align_end: if ws_count == 1 { align_end.unwrap() } else { false },
253 }
254 }
255
256 pub fn from_strings<I>(inputs: I) -> Result<Self, String>
259 where
260 I: IntoIterator,
261 I::Item: AsRef<str>, {
262 let parsed_items = ValidColumnItem::parse_vec(inputs)?;
264
265 Ok(Self::get(parsed_items))
267 }
268
269 pub fn format_item(&self, u: (usize, usize, usize, usize), row: &job::RawRow, tab: &[job::RawRow]) -> String {
270 let (index, num_width, tier_max, name_len_max) = u;
271 let mut parts = Vec::new();
272 let num_str = format!("{:>width$}.", index + 1, width = num_width);
273
274 if self.num_is_first {
276 parts.push(num_str.clone());
277 }
278
279 if self.list != ValidModeItemList::None {
281 let l = job::gens::item_list::draw_list(&self.list, index, tab, tier_max);
282 parts.push(l);
283 }
284
285 if self.icons != ValidModeItemIcons::None {
287 let ic = job::gens::draw_icon(&self.icons, &row.node.node, &row.name_with_ext);
288 parts.push(ic.to_string());
289 }
290
291 if !self.num_is_first {
293 parts.push(num_str);
294 }
295
296 if self.name {
298 parts.push(row.name_with_ext.clone());
299 }
300
301 let mut base = parts.join(" ");
302
303 if self.align_end {
305 let p_tier = (tier_max - row.node.tier) * 3;
306 let p_name = name_len_max - row.node.name.chars().count();
307 base.push_str(&" ".repeat(p_tier + p_name));
308 }
309
310 base
311 }
312}
313
314#[derive(Debug, Clone, PartialEq, Eq)]
319pub enum ValidTableColumns {
320 Date,
321 Time,
322 Size,
323 Item,
324 Path,
325}
326
327#[derive(Debug, Clone)]
328pub struct ValidTableColumnsConfig {
329 pub columns: Vec<ValidTableColumns>,
330}
331
332impl ValidTableColumns {
333 pub fn parse(s: &str) -> Result<Self, String> {
334 match s.trim().to_lowercase().as_str() {
335 "date" => Ok(ValidTableColumns::Date),
336 "time" => Ok(ValidTableColumns::Time),
337 "size" => Ok(ValidTableColumns::Size),
338 "item" => Ok(ValidTableColumns::Item),
339 "path" => Ok(ValidTableColumns::Path),
340 _ => Err(format!("Nieprawidłowa nazwa kolumny: '{}'. Dostępne: date, time, size, item, path", s.trim())),
341 }
342 }
343
344 pub fn parse_vec<I>(inputs: I) -> Result<Vec<Self>, String>
345 where
346 I: IntoIterator,
347 I::Item: AsRef<str>, {
348 inputs.into_iter().map(|s| Self::parse(s.as_ref())).collect()
349 }
350
351 pub fn parse_vec_as_config<I>(inputs: I) -> ValidTableColumnsConfig
352 where
353 I: IntoIterator,
354 I::Item: AsRef<str>, {
355 let mut columns = Vec::new();
356
357 let mut has_date = false;
358 let mut has_time = false;
359 let mut has_size = false;
360 let mut has_item = false;
361 let mut has_path = false;
362
363 let mut has_any_valid = false;
364
365 for item in inputs {
366 if let Ok(parsed) = Self::parse(item.as_ref()) {
367 has_any_valid = true;
368 match parsed {
369 ValidTableColumns::Date => {
370 if !has_date {
371 columns.push(ValidTableColumns::Date);
372 has_date = true;
373 }
374 }
375 ValidTableColumns::Time => {
376 if !has_time {
377 columns.push(ValidTableColumns::Time);
378 has_time = true;
379 }
380 }
381 ValidTableColumns::Size => {
382 if !has_size {
383 columns.push(ValidTableColumns::Size);
384 has_size = true;
385 }
386 }
387 ValidTableColumns::Item => {
388 if !has_item {
389 columns.push(ValidTableColumns::Item);
390 has_item = true;
391 }
392 }
393 ValidTableColumns::Path => {
394 if !has_path {
395 columns.push(ValidTableColumns::Path);
396 has_path = true;
397 }
398 }
399 }
400 }
401 }
402
403 if !has_any_valid {
405 return ValidTableColumnsConfig {
406 columns: vec![
407 ValidTableColumns::Date,
408 ValidTableColumns::Time,
409 ValidTableColumns::Size,
410 ValidTableColumns::Item,
411 ValidTableColumns::Path,
412 ],
413 };
414 }
415
416 if !has_item {
418 columns.push(ValidTableColumns::Item);
419 }
420
421 ValidTableColumnsConfig { columns }
422 }
423}
424
425#[derive(Debug, Clone, Copy, PartialEq, Eq)]
430pub enum ValidColumnSize {
431 Decimal, Binary, }
434
435#[derive(Debug, Clone)]
436pub struct ValidColumnSizeConfig {
437 pub mode: ValidColumnSize,
438}
439
440impl ValidColumnSize {
441 pub fn parse(s: &str) -> Result<Self, String> {
442 match s.trim().to_lowercase().as_str() {
443 "dec" | "decimal" => Ok(ValidColumnSize::Decimal),
444 "bin" | "binary" => Ok(ValidColumnSize::Binary),
445 _ => Err(format!("Nieznany system miar: '{}'. Dostępne: dec, bin", s.trim())),
446 }
447 }
448
449 pub fn parse_vec_as_config<I>(inputs: I) -> ValidColumnSizeConfig
450 where
451 I: IntoIterator,
452 I::Item: AsRef<str>, {
453 let mut mode = ValidColumnSize::Decimal; for item in inputs {
456 if let Ok(parsed) = Self::parse(item.as_ref()) {
457 mode = parsed;
458 break; }
460 }
461
462 ValidColumnSizeConfig { mode }
463 }
464}
465
466impl ValidColumnSizeConfig {
467 pub fn format_size(&self, bytes: u64) -> String {
469 let base = match self.mode {
470 ValidColumnSize::Decimal => 1000.0,
471 ValidColumnSize::Binary => 1024.0,
472 };
473
474 let suffix = match self.mode {
475 ValidColumnSize::Decimal => ["B ", "kB", "MB", "GB"],
476 ValidColumnSize::Binary => ["B ", "KiB", "MiB", "GiB"], };
478
479 let bytes_f = bytes as f64;
480
481 if bytes_f < base {
482 format!("{:>6} {}", bytes, suffix[0])
483 } else if bytes_f < base.powi(2) {
484 format!("{:>6.2} {}", bytes_f / base, suffix[1])
485 } else if bytes_f < base.powi(3) {
486 format!("{:>6.2} {}", bytes_f / base.powi(2), suffix[2])
487 } else {
488 format!("{:>6.2} {}", bytes_f / base.powi(3), suffix[3])
489 }
490 }
491}
492#[derive(Debug, Clone, Copy, PartialEq, Eq)]
497pub enum ValidColumnDate {
498 Default,
499}
500
501#[derive(Debug, Clone)]
502pub struct ValidColumnDateConfig {
503 pub format: String,
504}
505
506impl ValidColumnDate {
507 pub fn parse(s: &str) -> Result<Self, String> {
508 match s.trim().to_lowercase().as_str() {
509 "date-default" | "default" => Ok(ValidColumnDate::Default),
510 _ => Err(format!("Nieznany format daty: '{}'", s.trim())),
511 }
512 }
513
514 pub fn parse_vec_as_config<I>(inputs: I) -> ValidColumnDateConfig
515 where
516 I: IntoIterator,
517 I::Item: AsRef<str>, {
518 let format = "%Y W%V %u-%a".to_string(); for item in inputs {
521 if let Ok(_) = Self::parse(item.as_ref()) {
522 break;
524 }
525 }
526
527 ValidColumnDateConfig { format }
528 }
529}
530
531impl ValidColumnDateConfig {
532 pub fn format_date(&self, dt: chrono::DateTime<chrono::Local>) -> String { dt.format(&self.format).to_string() }
533}
534
535#[derive(Debug, Clone, Copy, PartialEq, Eq)]
540pub enum ValidColumnTime {
541 Default,
542}
543
544#[derive(Debug, Clone)]
545pub struct ValidColumnTimeConfig {
546 pub format: String,
547}
548
549impl ValidColumnTime {
550 pub fn parse(s: &str) -> Result<Self, String> {
551 match s.trim().to_lowercase().as_str() {
552 "time-default" | "default" => Ok(ValidColumnTime::Default),
553 _ => Err(format!("Nieznany format czasu: '{}'", s.trim())),
554 }
555 }
556
557 pub fn parse_vec_as_config<I>(inputs: I) -> ValidColumnTimeConfig
558 where
559 I: IntoIterator,
560 I::Item: AsRef<str>, {
561 let format = "%H:%M:%S.%3f".to_string(); for item in inputs {
564 if let Ok(_) = Self::parse(item.as_ref()) {
565 break;
566 }
567 }
568
569 ValidColumnTimeConfig { format }
570 }
571}
572
573impl ValidColumnTimeConfig {
574 pub fn format_time(&self, dt: chrono::DateTime<chrono::Local>) -> String { dt.format(&self.format).to_string() }
575}
576#[derive(Debug, Clone)]
581pub struct ValidPatternConfig {
582 pub patterns: Vec<String>,
583 pub ignore_case: bool,
584}
585
586impl ValidPatternConfig {
587 pub fn get(&self) -> logic::PatternsQueries {
589 let patterns_ref: Vec<&str> = self.patterns.iter().map(|s| s.as_str()).collect();
591 logic::PatternsQueries::new(patterns_ref, self.ignore_case)
592 }
593}
594
595pub struct ValidPattern;
596
597impl ValidPattern {
598 pub fn parse_vec_as_config<I>(inputs: I, ignore_case_sensitive: Option<bool>) -> ValidPatternConfig
599 where
600 I: IntoIterator,
601 I::Item: AsRef<str>, {
602 let patterns: Vec<String> = inputs.into_iter().map(|s| s.as_ref().to_string()).collect();
603
604 if patterns.is_empty() {
606 return ValidPatternConfig {
607 patterns: vec![
608 "./{.rustfmt,Cargo,rust-toolchain,Makefile}.toml&/".to_string(),
609 "./**/*.rs&/".to_string(),
610 "!./target/**".to_string(),
611 "!./.git/**".to_string(),
612 "./.{gitattributes,gitignore}".to_string(),
613 "./.github/workflows/*.yml&/".to_string(),
614 "./.vscode/settings.json&/".to_string(),
615 "./{API,ARCHITECTURE,AUTHORS,CHANGELOG,README,ROADMAP,TODO}.md".to_string(),
616 "./dist/**".to_string(),
617 ],
618 ignore_case: ignore_case_sensitive.unwrap_or(false),
619 };
620 }
621
622 ValidPatternConfig { patterns, ignore_case: ignore_case_sensitive.unwrap_or(false) }
623 }
624}
625
626#[derive(Debug, Clone)]
631pub struct ValidWorkspaceConfig {
632 pub workspace_dir: String,
633}
634
635impl ValidWorkspaceConfig {
636 pub fn get(&self) -> logic::AnchoredPathsDatum {
639 let path = PathBuf::from(&self.workspace_dir);
641
642 let work_path = match path.canonicalize() {
645 Ok(p) => p.to_string_lossy().to_string(),
646 Err(_) => {
647 match std::env::current_dir() {
650 Ok(current) => current.join(&path).to_string_lossy().to_string(),
651 Err(_) => self.workspace_dir.clone(),
652 }
653 }
654 };
655
656 logic::AnchoredPathsDatum::new(&work_path).unwrap_or_else(|x| {
658 eprintln!("❌ Błąd lokalizacji workspace: {}", x);
659 process::exit(1);
660 })
661 }
662}
663
664pub struct ValidWorkspace;
665
666impl ValidWorkspace {
667 pub fn parse_vec_as_config<I>(inputs: I) -> ValidWorkspaceConfig
668 where
669 I: IntoIterator,
670 I::Item: AsRef<str>, {
671 let mut workspace_dir = ".".to_string();
672
673 for item in inputs {
675 let s = item.as_ref().trim();
676 if !s.is_empty() {
677 workspace_dir = s.to_string();
678 break;
679 }
680 }
681
682 ValidWorkspaceConfig { workspace_dir }
683 }
684}
685