1use crate::lib::logic::config_model::*;
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum TabColumn {
10 TreeList, Number, Icon, Size, Date, Time, Path, }
18
19impl TabColumn {
20 pub fn parse(s: &str) -> Result<Self, String> {
21 match s.trim().to_lowercase().as_str() {
22 "treelist" | "tree" | "list" => Ok(TabColumn::TreeList),
23 "number" => Ok(TabColumn::Number),
24 "icon" => Ok(TabColumn::Icon),
25 "size" => Ok(TabColumn::Size),
26 "date" => Ok(TabColumn::Date),
27 "time" => Ok(TabColumn::Time),
28 "path" => Ok(TabColumn::Path),
29 _ => Err(format!(
30 "Nieprawidłowa nazwa kolumny: '{}'. Dostępne: list, tree, number, icon, size, date, time, path",
31 s.trim()
32 )),
33 }
34 }
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum TabSortBy {
41 Name,
42 Size,
43 Date,
44 Kind,
45 FileFirst,
46 DirFirst,
47 FileFirstMerge,
48 DirFirstMerge,
49}
50
51impl TabSortBy {
52 pub fn parse(s: &str) -> Result<Self, String> {
53 match s.trim().to_lowercase().as_str() {
54 "name" => Ok(TabSortBy::Name),
55 "size" => Ok(TabSortBy::Size),
56 "date" => Ok(TabSortBy::Date),
57 "kind" => Ok(TabSortBy::Kind),
58 "file-first" => Ok(TabSortBy::FileFirst),
59 "dir-first" => Ok(TabSortBy::DirFirst),
60 "file-merge" => Ok(TabSortBy::FileFirstMerge),
61 "dir-merge" => Ok(TabSortBy::DirFirstMerge),
62 _ => Err(format!(
63 "Nieprawidłowa wartość sortowania: '{}'. Dostępne: name, size, date, kind, file-first, dir-first, file-merge, dir-merge",
64 s.trim()
65 )),
66 }
67 }
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub enum TabSortOrder {
74 Asc,
75 Desc,
76}
77
78impl TabSortOrder {
79 pub fn parse(s: &str) -> Result<Self, String> {
80 match s.trim().to_lowercase().as_str() {
81 "az" | "asc" | "ascending" => Ok(TabSortOrder::Asc),
82 "za" | "desc" | "descending" => Ok(TabSortOrder::Desc),
83 _ => Err(format!("Nieprawidłowy kierunek sortowania: '{}'. Dostępne: asc, desc", s)),
84 }
85 }
86}
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub enum TabPathStructure {
91 List,
92 Tree,
93}
94
95impl TabPathStructure {
96 pub fn parse(s: &str) -> Result<Self, String> {
97 match s.trim().to_lowercase().as_str() {
98 "tree" | "t" | "T" => Ok(TabPathStructure::Tree),
99 "list" | "l" | "L" => Ok(TabPathStructure::List),
100 _ => Err(format!("Nieprawidłowa struktura: '{}'. Dostępne: tree, list", s)),
101 }
102 }
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
108pub enum JobMode {
109 Matched,
110 Mismatched,
111}
112
113impl JobMode {
114 pub fn parse(s: &str) -> Result<Self, String> {
115 match s.trim().to_lowercase().as_str() {
116 "m" | "M" | "matched" => Ok(JobMode::Matched),
117 "x" | "X" | "mismatched" => Ok(JobMode::Mismatched),
118 _ => Err(format!("Nieprawidłowy tryb zadania (mode): '{}'. Dostępne: m, x", s)),
119 }
120 }
121}
122
123#[derive(Debug, Clone, PartialEq, Eq)]
126pub struct TabSpec {
127 pub sort_by: TabSortBy,
128 pub sort_order: TabSortOrder,
129 pub structure: TabPathStructure,
130 pub columns: Vec<TabColumn>,
131 pub trim_size: Option<usize>,
132 pub trim_page: usize, pub more_icons: bool,
134}
135
136impl Default for TabSpec {
137 fn default() -> Self {
138 Self {
139 sort_by: TabSortBy::Name,
140 sort_order: TabSortOrder::Asc,
141 structure: TabPathStructure::Tree,
142 columns: vec![
143 TabColumn::Date,
144 TabColumn::Time,
145 TabColumn::Size,
146 TabColumn::TreeList,
147 TabColumn::Icon,
148 TabColumn::Number,
149 TabColumn::Path,
150 ],
151 trim_size: None,
152 trim_page: 1,
153 more_icons: false,
154 }
155 }
156}
157
158impl TabSpec {
159 pub fn from_config(cfg: &ConfigLayout, spec: &ConfigSpec) -> Result<Self, String> {
160 let mut columns = Vec::new();
161 for col_str in &cfg.columns {
162 columns.push(TabColumn::parse(col_str)?);
163 }
164
165 let mut tab = Self::default()
166 .sort(
167 TabSortBy::parse(&cfg.sort_by)?, TabSortOrder::parse(&cfg.sort_order)?, TabPathStructure::parse(&cfg.structure)?,
170 )
171 .columns(&columns)
172 .more_icons(cfg.more_icons);
173
174 if cfg.reverse {
176 tab.sort_order = TabSortOrder::Desc;
177 }
178
179 tab.trim_page = cfg.trim_page;
181
182 if let Some(trim) = &spec.trimming
183 && let Some(size) = trim.page_size
184 {
185 tab = tab.trim(size, trim.show_page);
186 }
187 Ok(tab)
188 }
189
190 pub fn sort(mut self, by: TabSortBy, order: TabSortOrder, structure: TabPathStructure) -> Self {
191 self.sort_by = by;
192 self.sort_order = order;
193 self.structure = structure;
194 self
195 }
196
197 pub fn columns(mut self, cols: &[TabColumn]) -> Self {
198 self.columns = cols.to_vec();
199 self
200 }
201
202 pub fn trim(mut self, size: usize, page: Option<usize>) -> Self {
203 self.trim_size = Some(size);
204 if let Some(p) = page {
205 self.trim_page = p;
206 }
207 self
208 }
209
210 pub fn more_icons(mut self, enabled: bool) -> Self {
211 self.more_icons = enabled;
212 self
213 }
214}
215
216#[derive(Debug, Clone, PartialEq, Eq)]
219pub struct ScanSpec {
220 pub work_path: String,
221 pub patterns: Vec<String>,
222 pub ignore_case: bool,
223}
224
225impl Default for ScanSpec {
226 fn default() -> Self {
227 Self {
228 work_path: ".".to_string(),
229 patterns: vec![
230 "./{.rustfmt,Cargo,rust-toolchain,Makefile}.toml&/".to_string(),
231 "./**/*.rs&/".to_string(),
232 "!./target/**".to_string(),
233 "!./.git/**".to_string(),
234 "./.{gitattributes,gitignore}".to_string(),
235 "./.github/workflows/*.yml&/".to_string(),
236 "./.vscode/settings.json&/".to_string(),
237 "./{API,ARCHITECTURE,AUTHORS,CHANGELOG,README,ROADMAP,TODO}.md".to_string(),
238 ],
239 ignore_case: false,
240 }
241 }
242}
243
244impl ScanSpec {
245 pub fn new(work_path: impl Into<String>) -> Self { Self { work_path: work_path.into(), ..Default::default() } }
246
247 pub fn from_config(cfg: &ConfigPattern) -> Self {
248 Self::new(&cfg.work_path)
249 .patterns(cfg.patterns.iter().map(|s| s.as_str()).collect())
250 .ignore_case(cfg.ignore_case)
251 }
252
253 pub fn patterns(mut self, pats: Vec<&str>) -> Self {
254 self.patterns = pats.into_iter().map(|s| s.to_string()).collect();
255 self
256 }
257
258 pub fn ignore_case(mut self, ignore: bool) -> Self {
259 self.ignore_case = ignore;
260 self
261 }
262}
263
264#[derive(Debug, Clone, PartialEq, Eq)]
267pub struct JobSpec {
268 pub id: String,
269 pub name: Option<String>,
270 pub description: Option<String>,
271 pub mode: JobMode,
273 pub scan: ScanSpec,
274 pub save_sotc_at: Option<String>,
276 pub title_sotc: Option<String>,
277 pub save_cots_at: Option<String>,
278 pub title_cots: Option<String>,
279 pub quiet_work: bool,
281 pub hide_stats: bool,
282 pub hide_promo: bool,
283 pub table: TabSpec,
285}
286
287impl Default for JobSpec {
288 fn default() -> Self {
289 Self {
290 id: "default_job".to_string(),
291 name: None,
292 description: None,
293 mode: JobMode::Matched,
295 scan: ScanSpec::default(),
296 save_sotc_at: None,
297 title_sotc: None,
298 save_cots_at: None,
299 title_cots: None,
300 quiet_work: false,
301 hide_stats: false,
302 hide_promo: false,
303 table: TabSpec::default(), }
305 }
306}
307
308impl JobSpec {
309 pub fn new(id: impl Into<String>) -> Self { Self { id: id.into(), ..Default::default() } }
310
311 pub fn from_config(cfg: &ConfigJob) -> Result<Self, String> {
312 let scan = ScanSpec::from_config(&cfg.scan);
313 let table = TabSpec::from_config(&cfg.table_sotc, &cfg.spec)?;
314
315 let mut job = Self::new(&cfg.id)
316 .scan(scan)
317 .table(table)
318 .mode(JobMode::parse(&cfg.spec.mode)?)
319 .quiet(cfg.quiet_work)
320 .hide_stats(cfg.spec.hide_stats)
321 .hide_promo(cfg.spec.hide_promo);
322
323 job.name = cfg.name.clone();
324 job.description = cfg.description.clone();
325
326 if let Some(export) = &cfg.export {
327 job.save_sotc_at = export.save_sotc_at.clone();
328 job.title_sotc = export.title_sotc.clone();
329 job.save_cots_at = export.save_cots_at.clone();
330 job.title_cots = export.title_cots.clone();
331 }
332 Ok(job)
333 }
334
335 pub fn work_path(mut self, path: impl Into<String>) -> Self {
336 self.scan.work_path = path.into();
337 self
338 }
339
340 pub fn patterns(mut self, pats: Vec<&str>) -> Self {
341 self.scan.patterns = pats.into_iter().map(|s| s.to_string()).collect();
342 self
343 }
344
345 pub fn ignore_case(mut self, ignore: bool) -> Self {
346 self.scan.ignore_case = ignore;
347 self
348 }
349
350 pub fn scan(mut self, scan_spec: ScanSpec) -> Self {
351 self.scan = scan_spec;
352 self
353 }
354
355 pub fn mode(mut self, mode: JobMode) -> Self {
356 self.mode = mode;
357 self
358 }
359
360 pub fn quiet(mut self, quiet: bool) -> Self {
361 self.quiet_work = quiet;
362 self
363 }
364
365 pub fn hide_stats(mut self, hide: bool) -> Self {
366 self.hide_stats = hide;
367 self
368 }
369
370 pub fn hide_promo(mut self, hide: bool) -> Self {
371 self.hide_promo = hide;
372 self
373 }
374
375 pub fn trim(mut self, size: usize, page: Option<usize>) -> Self {
377 self.table = self.table.trim(size, page);
378 self
379 }
380
381 pub fn table(mut self, tab_spec: TabSpec) -> Self {
383 self.table = tab_spec;
384 self
385 }
386}
387
388