1use std::{fs, path::Path};
2
3use anyhow::{Context, Result};
4use chrono::DateTime;
5
6use crate::lib::{job, logic};
7pub trait AsScannedNode {
13 fn as_scanned_node(&self) -> logic::ScannedNode;
14}
15
16impl AsScannedNode for logic::ScannedFileNode {
17 fn as_scanned_node(&self) -> logic::ScannedNode {
18 logic::ScannedNode {
19 name: self.name.clone(),
20 path: self.path.clone(),
21 node: logic::NodeIs::File,
22 tier: self.tier,
23 id_self: self.id_self, id_path: self.id_path.clone(), dir_has_subdirs: None,
26 dir_has_files_binary: None,
27 dir_has_files_text: None,
28 dir_has_symlinks: None,
29 file_is_binary: Some(self.is_binary),
30 file_is_empty: Some(self.is_empty),
31 }
32 }
33}
34
35impl AsScannedNode for logic::ScannedDirNode {
36 fn as_scanned_node(&self) -> logic::ScannedNode {
37 logic::ScannedNode {
38 name: self.name.clone(),
39 path: self.path.clone(),
40 node: logic::NodeIs::Dir,
41 tier: self.tier,
42 id_self: self.id_self, id_path: self.id_path.clone(), dir_has_subdirs: Some(self.has_subdirs),
45 dir_has_files_binary: Some(self.has_files_binary),
46 dir_has_files_text: Some(self.has_files_text),
47 dir_has_symlinks: Some(self.has_symlinks),
48 file_is_binary: None,
49 file_is_empty: None,
50 }
51 }
52}
53
54fn piece_gather<L>(j: &job::ValidPreparedJobConfig, p: &logic::Partition<L>) -> Vec<job::ValidResultMainRow>
60where
61 L: logic::MatchLabel,
62 L::Node: AsScannedNode, {
64 p.nodes
65 .iter()
66 .filter_map(|n| {
67 let unified_node = n.as_scanned_node();
68 data_inspect(&j, unified_node).ok() })
70 .collect()
71}
72
73fn data_inspect(j: &job::ValidPreparedJobConfig, scanned_node: logic::ScannedNode) -> Result<job::ValidResultMainRow> {
75 let clean_rel = scanned_node.path.str.strip_prefix("./").unwrap_or(&scanned_node.path.str);
78 let absolute_path = j.work.execution_dir.join(clean_rel);
79
80 let metadata = fs::metadata(&absolute_path).context("Nie można odczytać metadanych")?;
82
83 let dt_modified = DateTime::from(metadata.modified()?);
85
86 let name_with_ext = absolute_path.file_name().unwrap_or_default().to_string_lossy().to_string();
87
88 let size_real = if metadata.is_dir() { get_dir_size(&absolute_path) } else { metadata.len() };
89
90 Ok(job::ValidResultMainRow { dt_modified, name_with_ext, size_real, node: scanned_node })
91}
92
93fn get_dir_size(path: &Path) -> u64 {
95 walkdir::WalkDir::new(path)
96 .into_iter()
97 .filter_map(|e| e.ok())
98 .filter(|e| e.file_type().is_file())
99 .filter_map(|e| e.metadata().ok())
100 .map(|m| m.len())
101 .sum()
102}
103
104pub fn data_gather(j: &job::ValidPreparedJobConfig, b: &logic::PartitionScanned) -> job::ValidResultMainTab {
108 let mut final_rows = Vec::new();
109 let mut t_max = 0;
110 let mut n_max = 0;
111 let mut p_max = 0;
112
113 if j.part.md {
115 final_rows.extend(piece_gather(&j, &b.m_d));
116 t_max = t_max.max(b.m_d.tier_max);
117 n_max = n_max.max(b.m_d.name_len_max);
118 p_max = p_max.max(b.m_d.path_len_max);
119 }
120 if j.part.mf {
121 final_rows.extend(piece_gather(&j, &b.m_f));
122 t_max = t_max.max(b.m_f.tier_max);
123 n_max = n_max.max(b.m_f.name_len_max);
124 p_max = p_max.max(b.m_f.path_len_max);
125 }
126 if j.part.xd {
127 final_rows.extend(piece_gather(&j, &b.x_d));
128 t_max = t_max.max(b.x_d.tier_max);
129 n_max = n_max.max(b.x_d.name_len_max);
130 p_max = p_max.max(b.x_d.path_len_max);
131 }
132 if j.part.xf {
133 final_rows.extend(piece_gather(&j, &b.x_f));
134 t_max = t_max.max(b.x_f.tier_max);
135 n_max = n_max.max(b.x_f.name_len_max);
136 p_max = p_max.max(b.x_f.path_len_max);
137 }
138
139 final_rows.sort_unstable_by(|row_a, row_b| row_a.node.path.str.cmp(&row_b.node.path.str));
141
142 job::ValidResultMainTab { rows: final_rows, tier_max: t_max, name_len_max: n_max, path_len_max: p_max }
143}
144pub fn engine_step1_scanner(j: &job::ValidPreparedJobConfig) -> job::ValidResultMainTab {
147 let partition_scanned = logic::PartitionScanned::scan(&j);
149
150 data_gather(&j, &partition_scanned)
152}