Skip to main content

plot/lib/job/
step1.rs

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