Skip to main content

plot/lib/job/
step1.rs

1use std::{fs, path::Path};
2
3use anyhow::{Context, Result};
4use chrono::DateTime;
5
6use crate::lib::{job, logic};
7
8// ============================================================================
9// TRAIT POMOCNICZY (Unifikacja typów węzłów)
10// ============================================================================
11
12/// Pozwala na bezproblemową konwersję specjalistycznych węzłów na węzeł uniwersalny.
13pub trait AsScannedNode {
14	fn as_scanned_node(&self) -> logic::ScannedNode;
15}
16
17impl AsScannedNode for logic::ScannedFileNode {
18	fn as_scanned_node(&self) -> logic::ScannedNode {
19		logic::ScannedNode {
20			name: self.name.clone(),
21			path: self.path.clone(),
22			node: logic::NodeIs::File,
23			tier: self.tier,
24			id_self: self.id_self,         // ⚡ PRZEPISANIE ID
25			id_path: self.id_path.clone(), // ⚡ PRZEPISANIE ŚCIEŻKI
26			dir_has_subdirs: None,
27			dir_has_files_binary: None,
28			dir_has_files_text: None,
29			dir_has_symlinks: None,
30			file_is_binary: Some(self.is_binary),
31			file_is_empty: Some(self.is_empty),
32		}
33	}
34}
35
36impl AsScannedNode for logic::ScannedDirNode {
37	fn as_scanned_node(&self) -> logic::ScannedNode {
38		logic::ScannedNode {
39			name: self.name.clone(),
40			path: self.path.clone(),
41			node: logic::NodeIs::Dir,
42			tier: self.tier,
43			id_self: self.id_self,         // ⚡ PRZEPISANIE ID
44			id_path: self.id_path.clone(), // ⚡ PRZEPISANIE ŚCIEŻKI
45			dir_has_subdirs: Some(self.has_subdirs),
46			dir_has_files_binary: Some(self.has_files_binary),
47			dir_has_files_text: Some(self.has_files_text),
48			dir_has_symlinks: Some(self.has_symlinks),
49			file_is_binary: None,
50			file_is_empty: None,
51		}
52	}
53}
54
55// ============================================================================
56// GATHER & INSPECT (Faza Hydracji)
57// ============================================================================
58
59/// Przechodzi po całej partycji, konwertuje generyczne węzły i uderza do dysku po metadane.
60fn piece_gather<L>(a: &logic::AnchoredPathsDatum, p: &logic::Partition<L>) -> Vec<job::ValidResultMainRow>
61where
62	L: logic::MatchLabel,
63	L::Node: AsScannedNode, //  Wymagamy, aby węzeł w partycji potrafił stać się ScannedNode
64{
65	p.nodes
66		.iter()
67		.filter_map(|n| {
68			let unified_node = n.as_scanned_node();
69			data_inspect(a, unified_node).ok() // Ignorujemy pliki, które np. usunięto w międzyczasie
70		})
71		.collect()
72}
73
74/// Zderza zunifikowany węzeł z fizycznym dyskiem i buduje ostateczny wiersz.
75fn data_inspect(a: &logic::AnchoredPathsDatum, scanned_node: logic::ScannedNode) -> Result<job::ValidResultMainRow> {
76	// 1. Ustalenie fizycznej ścieżki absolutnej
77	// ⁉️ poco ponownie ustalamy
78	let clean_rel = scanned_node.path.str.strip_prefix("./").unwrap_or(&scanned_node.path.str);
79	let absolute_path = a.workspace_dir.buf.join(clean_rel);
80
81	// 2. Pobranie metadanych systemu plików
82	let metadata = fs::metadata(&absolute_path).context("Nie można odczytać metadanych")?;
83
84	// 3. Ekstrakcja danych
85	let dt_modified = DateTime::from(metadata.modified()?);
86
87	let name_with_ext = absolute_path.file_name().unwrap_or_default().to_string_lossy().to_string();
88
89	let size_real = if metadata.is_dir() { get_dir_size(&absolute_path) } else { metadata.len() };
90
91	Ok(job::ValidResultMainRow { dt_modified, name_with_ext, size_real, node: scanned_node })
92}
93
94/// Oblicza faktyczny (fizyczny) rozmiar katalogu sumując wszystkie pliki wewnątrz.
95fn get_dir_size(path: &Path) -> u64 {
96	walkdir::WalkDir::new(path)
97		.into_iter()
98		.filter_map(|e| e.ok())
99		.filter(|e| e.file_type().is_file())
100		.filter_map(|e| e.metadata().ok())
101		.map(|m| m.len())
102		.sum()
103}
104
105//============================================================================
106//job::ValidTablePartConfig
107
108pub fn data_gather(
109	a: &logic::AnchoredPathsDatum,
110	b: &logic::PartitionScanned,
111	c: job::ValidTablePartConfig,
112) -> job::ValidResultMainTab {
113	let mut final_rows = Vec::new();
114	let mut t_max = 0;
115	let mut n_max = 0;
116	let mut p_max = 0;
117
118	// Leniwe pobieranie danych - uderzamy do dysku tylko po wybrane pule
119	if c.md {
120		final_rows.extend(piece_gather(a, &b.m_d));
121		t_max = t_max.max(b.m_d.tier_max);
122		n_max = n_max.max(b.m_d.name_len_max);
123		p_max = p_max.max(b.m_d.path_len_max);
124	}
125	if c.mf {
126		final_rows.extend(piece_gather(a, &b.m_f));
127		t_max = t_max.max(b.m_f.tier_max);
128		n_max = n_max.max(b.m_f.name_len_max);
129		p_max = p_max.max(b.m_f.path_len_max);
130	}
131	if c.xd {
132		final_rows.extend(piece_gather(a, &b.x_d));
133		t_max = t_max.max(b.x_d.tier_max);
134		n_max = n_max.max(b.x_d.name_len_max);
135		p_max = p_max.max(b.x_d.path_len_max);
136	}
137	if c.xf {
138		final_rows.extend(piece_gather(a, &b.x_f));
139		t_max = t_max.max(b.x_f.tier_max);
140		n_max = n_max.max(b.x_f.name_len_max);
141		p_max = p_max.max(b.x_f.path_len_max);
142	}
143
144	// Sortujemy złączony wynik, aby przywrócić spójną strukturę drzewa po ścieżkach
145	final_rows.sort_unstable_by(|row_a, row_b| row_a.node.path.str.cmp(&row_b.node.path.str));
146
147	job::ValidResultMainTab { rows: final_rows, tier_max: t_max, name_len_max: n_max, path_len_max: p_max }
148}
149//============================================================================
150/// [Step 1] Skaner - teraz przyjmuje konfiguratory zamiast surowych struktur Job
151pub fn engine_step1_scanner(
152	c: job::ValidTablePartConfig,
153	workspace: &job::ValidWorkspaceConfig, // Zamiast ScanRawJob
154	patterns: &job::ValidPatternConfig,    // Zamiast ScanRawJob
155) -> job::ValidResultMainTab {
156	// 1. Korzystamy z Twoich nowych metod .get() - one robią całą magię inicjalizacji i walidacji
157	let anchored_paths_datum = workspace.get();
158	let patterns_queries = patterns.get();
159
160	// 2. Bezpośrednie wywołanie skanera z logiki
161	let partition_scanned = logic::PartitionScanned::scan(&anchored_paths_datum, &patterns_queries);
162
163	// 3. Zbieranie danych (hydracja z dysku) - funkcja data_gather pozostaje bez zmian
164	data_gather(&anchored_paths_datum, &partition_scanned, c)
165}