Skip to main content

plot/lib/job/gens/
item_list.rs

1use crate::lib::job::ValidModeItemList;
2// pub fn draw_list(mode: &ValidModeItemList, node: &ScannedNode) -> String {
3// 	match mode {
4// 		ValidModeItemList::Flat => "•".to_string(),
5// 		ValidModeItemList::Tree => {
6// 			// Tier 1 (root) nie ma wcięcia, Tier 2 ma jedno, itd.
7// 			let indent = "│  ".repeat(node.tier.saturating_sub(1));
8// 			format!("{}├──", indent)
9// 		}
10// 		ValidModeItemList::None => unreachable!("None jest obsługiwane lokalnie przed wywołaniem"),
11// 	}
12// }
13use crate::lib::{job::RawRow, logic::NodeIs};
14
15pub struct TreeLast;
16impl TreeLast {
17	pub const DIR_NO_CHILDREN: &'static str = "└───";
18	pub const DIR_WITH_CHILDREN: &'static str = "└──┬";
19	pub const FILE: &'static str = "└──•";
20	pub const INDENT: &'static str = "   ";
21}
22
23pub struct TreeMid;
24impl TreeMid {
25	pub const DIR_NO_CHILDREN: &'static str = "├───";
26	pub const DIR_WITH_CHILDREN: &'static str = "├──┬";
27	pub const FILE: &'static str = "├──•";
28	pub const INDENT: &'static str = "│  ";
29}
30
31pub struct DrawTree;
32impl DrawTree {
33	pub const ITEM_BETWEEN: &'static str = "  ├──•";
34	pub const ITEM_FIRST: &'static str = "  ┌──•";
35	pub const ITEM_LAST: &'static str = "  └──•";
36	pub const ITEM_ONEFOLD: &'static str = "   ──•";
37
38	pub fn list(index: usize, total: usize) -> &'static str {
39		if total <= 1 {
40			Self::ITEM_ONEFOLD
41		} else if index == 0 {
42			Self::ITEM_FIRST
43		} else if index == total - 1 {
44			Self::ITEM_LAST
45		} else {
46			Self::ITEM_BETWEEN
47		}
48	}
49}
50
51pub fn draw_list(mode: &ValidModeItemList, index: usize, tab: &[RawRow], _tier_max: usize) -> String {
52	match mode {
53		ValidModeItemList::Flat => DrawTree::list(index, tab.len()).to_string(),
54		ValidModeItemList::Tree => {
55			let node = &tab[index].node;
56			let t = node.tier;
57
58			// 1. Sprawdzamy czy ma wyrenderowane dzieci (Lookahead O(1))
59			// Jeśli następny element ma większy tier, to znaczy, że wpadł "do środka" tego folderu.
60			let is_dir = node.node == NodeIs::Dir;
61			let has_children = if is_dir {
62				if index + 1 < tab.len() { tab[index + 1].node.tier > t } else { false }
63			} else {
64				false
65			};
66
67			// 2. Lookahead O(n): obliczamy `is_last` dla węzła i wszystkich jego przodków
68			let mut is_last = vec![true; t + 1];
69			let mut active_levels = t;
70
71			for j in (index + 1)..tab.len() {
72				let next_tier = tab[j].node.tier;
73				if next_tier <= active_levels {
74					is_last[next_tier] = false;
75					active_levels = next_tier - 1;
76				}
77				if active_levels == 0 {
78					break;
79				}
80			}
81
82			// 3. Generujemy wcięcia (indents) dla przodków (od poziomu 1 do T-1)
83			let mut result = String::new();
84			for l in 1..t {
85				if is_last[l] {
86					result.push_str(TreeLast::INDENT);
87				} else {
88					result.push_str(TreeMid::INDENT);
89				}
90			}
91
92			// 4. Dobieramy odpowiedni symbol gałęzi końcowej
93			let am_i_last = is_last[t];
94			let branch = if is_dir {
95				match (am_i_last, has_children) {
96					(true, true) => TreeLast::DIR_WITH_CHILDREN,
97					(false, true) => TreeMid::DIR_WITH_CHILDREN,
98					(true, false) => TreeLast::DIR_NO_CHILDREN,
99					(false, false) => TreeMid::DIR_NO_CHILDREN,
100				}
101			} else if am_i_last {
102				TreeLast::FILE
103			} else {
104				TreeMid::FILE
105			};
106
107			result.push_str(branch);
108			result
109		}
110		ValidModeItemList::None => unreachable!("None jest obsługiwane lokalnie przed wywołaniem"),
111	}
112}