Skip to main content

plot/lib/job/
step3.rs

1use std::{
2	collections::BTreeMap,
3	path::{Path, PathBuf},
4};
5
6use crate::lib::job;
7
8// ============================================================================
9// SORTER (Step 3) - UKŁADACZ ELEMENTÓW W WORKU
10// ============================================================================
11
12pub fn engine_step3_data_sort(
13	sort_cfg: &job::ValidSortByConfig,
14	item_cfg: &job::ValidColumnItemConfig,
15	table: &mut job::ValidResultMainTab,
16) {
17	if table.rows.is_empty() {
18		return;
19	}
20
21	// Wyciągamy informacje o trybie płaskim bezpośrednio z konfiguratora
22	let is_flat = match item_cfg.list {
23		job::ModeListForValidColumnItem::Flat => true,
24		job::ModeListForValidColumnItem::None => true,
25		job::ModeListForValidColumnItem::Tree => false,
26	};
27
28	// 1. Inicjalizacja ciężkiej logiki sortowania
29	let queries = sort_cfg.get();
30
31	if is_flat {
32		// --------------------------------------------------------------------
33		// JEDEN WOREK: Globalne sortowanie płaskiej listy
34		// --------------------------------------------------------------------
35		table.rows.sort_by(|a, b| queries.compare(a, b));
36	} else {
37		// --------------------------------------------------------------------
38		// WIELE WORKÓW: Sortowanie w obrębie własnych katalogów (Tree)
39		// --------------------------------------------------------------------
40		let clean_paths: Vec<PathBuf> =
41			table.rows.iter().map(|r| PathBuf::from(r.node.path.str.trim_end_matches('/'))).collect();
42
43		// Mapa powiązań (Klucz = Rodzic, Wartość = Indeksy dzieci)
44		let mut tree_map: BTreeMap<PathBuf, Vec<usize>> = BTreeMap::new();
45
46		for (i, p) in clean_paths.iter().enumerate() {
47			let parent = p.parent().map_or_else(|| PathBuf::from("."), Path::to_path_buf);
48			tree_map.entry(parent).or_default().push(i);
49		}
50
51		// Sortujemy zawartość wewnątrz każdego worka z osobna!
52		for indices in tree_map.values_mut() {
53			indices.sort_by(|&a, &b| queries.compare(&table.rows[a], &table.rows[b]));
54		}
55
56		// Szukamy "Korzeni" (Głównych worków)
57		let mut root_indices = Vec::new();
58		for (i, p) in clean_paths.iter().enumerate() {
59			let parent = p.parent().map_or_else(|| PathBuf::from("."), Path::to_path_buf);
60			let is_root = parent == Path::new(".") || parent == Path::new("") || !clean_paths.contains(&parent);
61			if is_root {
62				root_indices.push(i);
63			}
64		}
65
66		// Sortujemy same korzenie między sobą
67		root_indices.sort_by(|&a, &b| queries.compare(&table.rows[a], &table.rows[b]));
68
69		// Zwijamy wszystko z powrotem do płaskiego wektora w idealnej kolejności
70		let mut flat_indices = Vec::with_capacity(table.rows.len());
71		flatten(&root_indices, &tree_map, &clean_paths, &mut flat_indices);
72
73		// Błyskawiczna podmiana wierszy (in-place)
74		let total_rows = table.rows.len(); // Najpierw czytamy długość...
75		let old_rows = std::mem::replace(&mut table.rows, Vec::with_capacity(total_rows)); // ...potem mutujemy!
76
77		let mut temp_rows: Vec<Option<job::ValidResultMainRow>> = old_rows.into_iter().map(Some).collect();
78
79		for idx in flat_indices {
80			if let Some(row) = temp_rows[idx].take() {
81				table.rows.push(row);
82			}
83		}
84	}
85}
86
87/// Helper do rekurencyjnego opróżniania worków w ustalonej kolejności
88fn flatten(indices: &[usize], tree_map: &BTreeMap<PathBuf, Vec<usize>>, clean_paths: &[PathBuf], out: &mut Vec<usize>) {
89	for &idx in indices {
90		out.push(idx);
91		if let Some(children) = tree_map.get(&clean_paths[idx]) {
92			flatten(children, tree_map, clean_paths, out);
93		}
94	}
95}