Skip to main content

plot/lib/schema/
job_raw_cli.rs

1use std::collections::BTreeSet;
2
3use clap::{Args, Parser};
4
5use super::SharedJobAttributeToSelect;
6// ============================================================================
7// GŁÓWNY PUNKT WEJŚCIA DLA WTYCZKI CARGO
8// ============================================================================
9
10#[derive(Parser, Debug, Clone)]
11#[command(name = "cargo", bin_name = "cargo", version, about, long_about = None)]
12pub enum CargoCliRoot {
13	#[command(name = "plot", about = "Szwajcarski scyzoryk do wizualizacji struktury projektu", version)]
14	Plot(RawCliJob),
15}
16
17// ============================================================================
18// STRUKTURA ARGUMENTÓW CLI
19// ============================================================================
20
21#[derive(Args, Debug, Clone)]
22// ⚡ TWARDA GRUPA: Wymusza użycie dokładnie jednej z tych trzech flag na start.
23#[command(group(
24    clap::ArgGroup::new("exec_mode")
25        .required(true)
26        .args(["job_blank", "jobs_load", "help_for_pattern", "help_for_config", "jobs_load_only_show"])
27))]
28pub struct RawCliJob {
29	// ========================================================================
30	// INFORMACJE I POMOC (Exclusive - blokują inne flagi)
31	// ========================================================================
32	/// Wyświetla szczegółową pomoc dotyczącą składni wzorców (Patterns)
33	#[arg(help_heading = "Information", short = 'P', long = "help-pattern")]
34	pub help_for_pattern: bool,
35
36	/// Wyświetla pomoc dotyczącą struktury pliku konfiguracyjnego TOML
37	#[arg(help_heading = "Information", short = 'T', long = "help-tasks-make")]
38	pub help_for_config: bool,
39	
40	/// Załadowywuje plik z konfiguracją zadania i tylko wyświetla listę zadań, nic nie robi więcej.
41	#[arg(
42        help_heading = "Information",
43        short = 'L',
44        long = "help-tasks-show",
45        num_args = 0..=1,
46        default_missing_value = ""
47    )]
48	pub jobs_load_only_show: Option<String>,
49
50	
51	// ========================================================================
52	// RESZTA OPCJI (Działają tylko logicznie w trybie -b, zgodnie z Twoim kodem)
53	// ========================================================================
54	/// Uruchom w trybie "suchy bieg" (dry run)
55	#[arg(help_heading = "Run Options",  short = 'U', long = "dry-run")]
56	pub dry_run: Option<bool>,
57
58	/// Nie zapisuj wyników na dysku
59	#[arg(help_heading = "Run Options",  short = 'N',long = "no-export", visible_aliases = ["no-save", "only-view"])]
60	pub export_nothing: Option<bool>,
61
62	/// Całkowita cisza - nie drukuje absolutnie niczego na ekranie (nawet błędów)
63	#[arg(help_heading = "Run Options", short = 'O', long = "clear")]
64	pub print_nothing: Option<bool>,
65
66	/// Pokaż tylko ostrzeżenia
67	#[arg(help_heading = "Run Options", short = 'W', long = "only-warnings")]
68	pub print_warnings: Option<bool>,
69
70	/// Zapisuje dodatkowe informacjie
71	#[arg(help_heading = "Run Options",  short = 'E', long = "inspection-in-export")]
72	pub inspection_in_export: Option<bool>,
73
74	/// Wyświetla dodatkowe informacje
75	#[arg(help_heading = "Run Options",  short = 'I', long = "inspection-in-print")]
76	pub inspection_in_print: Option<bool>,
77
78	// ░░░░░░░░░░ PODSTAWY (Sterowanie potokiem wg diagramu) ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
79
80
81	/// Załadowywuje plik z konfiguracją zadania w formacie TOML. Wymaga podania ścieżki lub użyje domyślnej po samej fladze.
82	#[arg(
83        help_heading = "Main Options",
84        short = 'l',
85        long = "jobs-load",
86        num_args = 0..=1,
87        default_missing_value = ""
88    )]
89	pub jobs_load: Option<String>,
90
91	/// Wybiera zadanie lub zadania do obsłużenia z pliku TOML. Opcjonalne, ma zastosowanie TYLKO przy użyciu flagi -l.
92	#[arg(
93        help_heading = "Main Options",
94        short = 't',
95        long = "job-ids",
96        value_parser = parse_ids, // ⚡ Podpięto parser
97        requires = "jobs_load" // ⚡ Twarde wymuszenie Clap: nie zadziała bez -l
98    )]
99	pub task_id: Option<BTreeSet<String>>,
100
101	/// Pozwala utworzyć nowe czyste zapytanie z domyślnymi ustawieniami, zamiast wczytywać z configu lub pliku TOML.
102	#[arg(help_heading = "Main Options", short = 'b', long = "job-blank")]
103	pub job_blank: bool,
104
105	/// Uruchamia zadanie w trybie przyspieszonym (tylko bazowe parametry, optymalna ścieżka wykonania)
106	#[arg(help_heading = "Main Options", short = 's', long = "speed", requires = "job_blank")]
107	pub speed: bool,
108
109	// ░░░░░░░░░░ EXPLORER ░░░░░░░░░░
110	/// Ścieżka katalogu do skanowania (domyślnie obecny katalog)
111	#[arg(help_heading = "Scan Options", short = 'd', long = "dir-to-scan", default_value = ".")]
112	pub dir_to_scan: String,
113
114	/// Wzorce wyszukiwania (np. \"src/**/*.rs\", \"!target/**\")
115	#[arg(help_heading = "Scan Options", short = 'p', long = "pattern", visible_aliases = ["pat", "patterns"], value_name = "GLOB")]
116	pub patterns: Vec<String>,
117
118	/// Części zapytania [MF, MD, XF, XD] (np. --parts md mf)
119	/// lub pełna nazwa [matched-files, matched-dirs, mismatched-files, mismatched-dirs]
120	#[arg(help_heading = "Scan Options", short = 'q', long = "query-parts", default_value = "mf, md")]
121	pub query_parts: Vec<String>,
122
123	/// Ignoruj wielkość liter przy dopasowywaniu wzorców
124	#[arg(help_heading = "Scan Options", short = 'i', long = "ignore-case")]
125	pub ignore_case: Option<bool>,
126
127	// ░░░░░░░░░░ EXPORT ░░░░░░░░░░
128
129	// wymagane jęsli save włączone
130	#[arg(
131		help_heading = "Save Options",
132		short = 'o',
133		long = "out-dir",
134		default_value = "./target/.cargo-plot/out/"
135	)]
136	pub out_dir: String,
137
138	#[arg(help_heading = "Save Options", short = 'u', long, help = "Tytuł raportu Markdown")]
139	pub title: Option<String>,
140
141	#[arg(help_heading = "Save Options", short = 'n', long, help = "Nazwa pliku wyjściowego")]
142	pub name: Option<String>,
143
144	#[arg(help_heading = "Save Options", short = 'X', long, help = "Czy nazwa ma być prefiksem zamiast sufiksu?")]
145	pub name_is_first: Option<bool>,
146
147	#[arg(help_heading = "Save Options", long, help = "Zapisz SOTC i COTS jako osobne pliki")]
148	pub save_separately: Option<bool>,
149
150	// ░░░░░░░░░░ THEME / LAYOUT ░░░░░░░░░░
151	
152	/// kolumna item jest obowiązkowa, jeśli brak ? - dodawan jest na końcu
153	/// Kolumny do wyświetlenia (oddzielone przecinkami: date,time,size,path,item)
154	#[arg(
155        short = 'c',
156        long = "columns",
157        value_parser = parse_attribute, // ⚡ Podpięto parser
158        value_delimiter = ',',
159        default_value = "date,time,size,path,item",
160        help_heading = "Layout Options"
161    )]
162	pub attributes: Option<Vec<SharedJobAttributeToSelect>>,
163
164	#[arg(help_heading = "Theme Options", long = "show-colors")]
165	pub show_colors: Option<bool>,
166
167	#[arg(help_heading = "Theme Options", long = "tree-hide")]
168	pub tree_hide: Option<bool>,
169
170	#[arg(help_heading = "Layout Options", long = "icons-hide", conflicts_with = "icon_more")]
171	pub icon_hide: Option<bool>,
172
173	#[arg(help_heading = "Layout Options", long = "name-hide")]
174	pub name_hide: Option<bool>,
175
176	#[arg(help_heading = "Layout Options", long = "align-hide")]
177	pub align_hide: Option<bool>,
178
179	#[arg(help_heading = "Theme Options", long = "icons-more", conflicts_with = "icon_hide")]
180	pub icon_more: Option<bool>,
181
182	#[arg(help_heading = "Theme Options", long = "num-before")]
183	pub num_before: Option<bool>,
184
185	#[arg(help_heading = "Theme Options", short ='B', long = "size-binary")]
186	pub size_binary: Option<bool>,
187
188	#[arg(help_heading = "Theme Options", short ='Y', long = "fmt-date")]
189	pub fmt_date: Option<String>,
190
191	#[arg(help_heading = "Theme Options", short ='H', long = "fmt-time")]
192	pub fmt_time: Option<String>,
193
194
195	// ░░░░░░░░░░ PILE OPTIONS ░░░░░░░░░░
196	/// Typ grupowania (Pile). Dostępne opcje: name, exte, none.
197	#[arg(help_heading = "Pile Options", long = "pile")]
198	pub pile: Option<String>,
199
200	/// Jeśli włączono grupowanie, katalogi zawsze wyświetlają się jako pierwsze.
201	#[arg(
202        help_heading = "Pile Options",
203        long = "dir-first",
204		short = 'D',
205        requires = "pile", // ⚡ Wymaga flagi --pile
206    )]
207	pub dir_first: Option<bool>,
208
209	/// Jeśli włączono grupowanie (name), zbiera pliki o tej samej nazwie obok katalogu nadrzędnego.
210	#[arg(
211        help_heading = "Pile Options",
212		short = 'G',
213        long = "name-nearby",
214        requires = "pile" // ⚡ Wymaga flagi --pile
215    )]
216	pub name_nearby: Option<bool>,
217
218	// ░░░░░░░░░░ SORT OPTIONS ░░░░░░░░░░
219	/// Kryterium sortowania: date, size, path, name, none.
220	#[arg(help_heading = "Sort Options", long = "sort", value_parser = ["date", "size", "path", "name", "none"])]
221	pub sort: Option<String>,
222
223	/// Odwróć kolejność sortowania (Descending). Wymaga --sort.
224	#[arg(help_heading = "Sort Options", short = 'R', long = "reverse", requires = "sort")]
225	pub reverse: Option<bool>,
226
227	/// Odbicie lustrzane sortowania tekstowego. Wymaga --sort (path/name).
228	#[arg(help_heading = "Sort Options", short = 'M', long = "mirror", requires = "sort")]
229	pub mirror: Option<bool>,
230
231	/// Strategia sortowania (Spec, Num, AZaz). Wymaga --sort (path/name).
232	#[arg(
233        help_heading = "Sort Options",
234        short = 'S',
235        long = "strategy",
236        requires = "sort",
237        value_parser = parse_strategy // ⚡ Walidacja 24 strategii
238    )]
239	pub strategy: Option<String>,
240}
241
242/// Customowy parser dla kolumn atrybutów, wspierający przyjazne dla użytkownika nazwy.
243fn parse_attribute(s: &str) -> Result<SharedJobAttributeToSelect, String> {
244	match s.to_lowercase().trim() {
245		"date" => Ok(SharedJobAttributeToSelect::Date),
246		"time" => Ok(SharedJobAttributeToSelect::Time),
247		"size" => Ok(SharedJobAttributeToSelect::Size),
248		"path" => Ok(SharedJobAttributeToSelect::Path),
249		"item" | "treelist" => Ok(SharedJobAttributeToSelect::Item), // Zgodnie ze starą notacją wszystko to jest de facto elementem Item
250		_ => Err(format!("Nieznana kolumna: '{}'. Dostępne to: date, time, size, path, item", s)),
251	}
252}
253
254/// Walidator strategii sortowania - dopuszcza tylko 24 predefiniowane kombinacje.
255fn parse_strategy(s: &str) -> Result<String, String> {
256	let valid_strategies = [
257		"Spec,Num,AZaz",
258		"Spec,Num,azAZ",
259		"Spec,Num,aAzZ",
260		"Spec,Num,AaZz",
261		"Num,Spec,AZaz",
262		"Num,Spec,azAZ",
263		"Num,Spec,aAzZ",
264		"Num,Spec,AaZz",
265		"Spec,AZaz,Num",
266		"Spec,azAZ,Num",
267		"Spec,aAzZ,Num",
268		"Spec,AaZz,Num",
269		"Num,AZaz,Spec",
270		"Num,azAZ,Spec",
271		"Num,aAzZ,Spec",
272		"Num,AaZz,Spec",
273		"AZaz,Num,Spec",
274		"azAZ,Num,Spec",
275		"aAzZ,Num,Spec",
276		"AaZz,Num,Spec",
277		"AZaz,Spec,Num",
278		"azAZ,Spec,Num",
279		"aAzZ,Spec,Num",
280		"AaZz,Spec,Num",
281	];
282
283	if valid_strategies.contains(&s) {
284		Ok(s.to_string())
285	} else {
286		Err(format!("Nieprawidłowa strategia: '{}'. Przykład poprawnej: 'Spec,Num,AaZz'", s))
287	}
288}
289
290/// Customowy parser dla identyfikatorów zadań (wspiera format "x1,q2")
291fn parse_ids(s: &str) -> Result<BTreeSet<String>, String> {
292	let ids: BTreeSet<String> = s.split(',').map(|id| id.trim().to_string()).filter(|id| !id.is_empty()).collect();
293
294	if ids.is_empty() {
295		Err("Nie podano prawidłowych identyfikatorów zadań. Użyj formatu np. 'x1,q2'".to_string())
296	} else {
297		Ok(ids)
298	}
299}