Skip to main content

plot/lib/logic/
engine.rs

1use crate::lib::logic::{
2	AnchoredPathsDatum,
3	DocMarkdown,
4	PartitioningResult,
5	PatternsToApply,
6	ScanSpec,
7	ScannedToApply,
8	TabSpec,
9	TableOutput,
10	TagTime,
11	tag_time,
12};
13
14#[derive(Clone, Copy)]
15pub enum MX {
16	M,
17	X,
18	Matched,
19	Mismatched,
20}
21
22#[derive(Clone, Copy)]
23pub struct RenderFlags {
24	pub hide_stats: bool,
25	pub hide_promo: bool,
26	pub mode: MX,
27}
28
29/// Główny silnik spinający skanowanie, filtrowanie i konfigurację widoku.
30pub struct DocEngine {
31	pub path: AnchoredPathsDatum,
32	pub tagtime: TagTime,
33	pub result: PartitioningResult,
34	pub last_render: Option<RenderFlags>,
35}
36
37impl DocEngine {
38	pub fn new(scan: ScanSpec) -> Self {
39		let anchored_paths_datum = AnchoredPathsDatum::new(&scan.work_path).unwrap_or_else(|x| {
40			eprintln!("❌ {}", x);
41			std::process::exit(1);
42		});
43
44		let scanned_to_apply = ScannedToApply::scan(&anchored_paths_datum);
45		let ref_patterns: Vec<&str> = scan.patterns.iter().map(|s| s.as_str()).collect();
46		let patterns_to_apply = PatternsToApply::new(ref_patterns, scan.ignore_case);
47
48		// let mut tab = PartitioningResult::new(scanned_to_apply, patterns_to_apply)
49		//    .sort(spec.sort_by, spec.sort_order, spec.structure)
50		//    .columns(&spec.columns);
51		// tab.spec = spec;
52		let partitioning_result = PartitioningResult::new(scanned_to_apply, patterns_to_apply);
53
54		Self { path: anchored_paths_datum, tagtime: tag_time(), result: partitioning_result, last_render: None }
55	}
56
57	pub fn spec(mut self, spec: TabSpec) -> Self {
58		self.result.spec = spec;
59		self
60	}
61
62	// ============================================================================
63	// Wewnętrzny silnik generujący
64	// ============================================================================
65
66	fn section_header(&self, border: String) -> String {
67		let pats = format!("{:?}", self.result.patterns.patterns.0);
68		let m_len = self.result.m.paths.len();
69		let x_len = self.result.x.paths.len();
70
71		let stat = &self.result.scanner.stat;
72		let dir_path = format!("\"{}\"", stat.relation.select_dir.buf.display());
73
74		let mut header = String::new();
75		header.push_str(&format!("{}\n", border));
76		header.push_str(&format!(
77			"📊 | 📝 {} | 📂 {} | ⭕ {} | ✔️  {} | ✖️  {} |\n",
78			stat.count_files, stat.count_folder, stat.count_empty, m_len, x_len
79		));
80		header.push_str(&format!("🔎 {}\n", pats));
81		header.push_str(&format!("🗃️  {}\n", dir_path));
82		header.push_str(&border);
83		header
84	}
85
86	fn section_footer(&self, border: String) -> String {
87		let mut footer = String::new();
88		footer.push_str("📚 | [Crates.io](https://crates.io/crates/cargo-plot) |\n");
89		footer.push_str("📚 | [GitHub](https://github.com/j-Cis/cargo-plot/releases) |\n");
90		// footer.push_str(&format!("Wersja: {}\n", self.version));
91		footer.push_str(&border);
92		footer
93	}
94
95	/// Składa w całość ostateczny ciąg znaków
96	fn build_structure_of_the_content(&self, flags: &RenderFlags) -> String {
97		let tab_out = match flags.mode {
98			MX::M | MX::Matched => self.result.build_matched(),
99			MX::X | MX::Mismatched => self.result.build_mismatched(),
100		};
101
102		let mut parts = Vec::with_capacity(3);
103
104		if !flags.hide_stats {
105			parts.push(self.section_header("░".repeat(80)));
106			parts.push(format!("{}", tab_out));
107			parts.push("░".repeat(80));
108		} else {
109			parts.push(format!("{}", tab_out));
110		}
111
112		if !flags.hide_promo {
113			parts.push(self.section_footer("░".repeat(80)));
114		}
115
116		// Łączymy wszystko znakami nowej linii i dodajemy na końcu
117		parts.join("\n") + "\n"
118	}
119
120	// ============================================================================
121	// KONFIGURACJA WIDOKU (Builder API)
122	// ============================================================================
123
124	#[inline]
125	fn finalize_view_structure_of_the_content(&mut self, flags: RenderFlags) {
126		self.last_render = Some(flags);
127		print!("{}", self.build_structure_of_the_content(&flags));
128	}
129
130	/// REZULTAT PEŁNY
131	pub fn view(mut self, tab_mode: MX, hide_stats: bool, hide_promo: bool) -> Self {
132		self.finalize_view_structure_of_the_content(RenderFlags { hide_stats, hide_promo, mode: tab_mode });
133		self
134	}
135
136	/// REZULTAT PRZYCIĘTY/POCIĘTY (Limit / Paginacja)
137	pub fn view_trimmed(
138		mut self,
139		tab_mode: MX,
140		hide_stats: bool,
141		hide_promo: bool,
142		size: usize,
143		page: Option<usize>, // Jeśli None, działa jak dawny Limit (strona 1)
144	) -> Self {
145		self.result.spec = self.result.spec.trim(size, page);
146		self.finalize_view_structure_of_the_content(RenderFlags { hide_stats, hide_promo, mode: tab_mode });
147		self
148	}
149
150	// ============================================================================
151	// UTILS "DRY"
152	// ============================================================================
153
154	/// Pobiera aktualne flagi renderowania lub zwraca domyślne
155	fn current_flags(&self) -> RenderFlags {
156		self.last_render.unwrap_or(RenderFlags { hide_stats: false, hide_promo: false, mode: MX::M })
157	}
158
159	/// Generuje surowy obiekt TableOutput na podstawie trybu z flag
160	fn generate_table(&self, mode: MX) -> TableOutput {
161		match mode {
162			MX::M | MX::Matched => self.result.build_matched(),
163			MX::X | MX::Mismatched => self.result.build_mismatched(),
164		}
165	}
166
167	/// Składa w jedną całość inicjalizację obiektu Markdown (redukcja powtórzeń
168	/// w metodach save)
169	fn init_markdown(&self, content: String, table: TableOutput) -> DocMarkdown {
170		DocMarkdown::new(
171			content,
172			table,
173			self.path.execut_dir.clone(),
174			self.path.select_dir.clone(),
175			self.tagtime.clone(),
176		)
177	}
178
179	// ============================================================================
180	// ZAPIS DO PLIKU
181	// ============================================================================
182
183	pub fn save_structure_of_the_content(self, rel_path: &str, title: Option<&str>) -> Self {
184		let flags = self.current_flags();
185		let table_output = self.generate_table(flags.mode);
186		let raw_out_str = self.build_structure_of_the_content(&flags);
187		let md = self.init_markdown(raw_out_str, table_output);
188
189		if let Err(e) = md.structure_of_the_content_save_as(rel_path, title) {
190			eprintln!("❌ Błąd zapisu SOTC (Struktura Zawartości): {}", e);
191		}
192		self
193	}
194
195	pub fn save_content_of_the_structure(self, rel_path: &str, title: Option<&str>) -> Self {
196		let flags = self.current_flags();
197		let table_output = self.generate_table(flags.mode);
198		let raw_out_str = self.build_structure_of_the_content(&flags);
199		let md = self.init_markdown(raw_out_str, table_output);
200
201		if let Err(e) = md.content_of_the_structure_save_as(rel_path, title) {
202			eprintln!("❌ Błąd zapisu COTS (Zawartość Struktury): {}", e);
203		}
204		self
205	}
206}