1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
use super::{NodeInfo, TreeColumnWidth, TreeRow, TreeTable};
use crate::{size::Size, visualizer::ProportionBar};
use assert_cmp::debug_assert_op;
use derive_more::{Deref, DerefMut};
use std::{collections::LinkedList, fmt::Display};

#[derive(Deref, DerefMut)]
pub(super) struct BarRow<Name, NodeData> {
    #[deref]
    #[deref_mut]
    pub(super) tree_row: TreeRow<Name, NodeData>,
    pub(super) proportion_bar: ProportionBar,
}

#[derive(Default, Clone, Copy, Deref, DerefMut)]
pub(super) struct BarColumnWidth {
    #[deref]
    #[deref_mut]
    pub(super) tree_column_width: TreeColumnWidth,
}

pub(super) fn render_bars<'a, Name, Data>(
    tree_table: TreeTable<&'a Name, Data>,
    total: u64,
    width: usize,
) -> LinkedList<BarRow<&'a Name, Data>>
where
    Name: Display,
    Data: Size + Into<u64> + 'a,
{
    tree_table
        .data
        .into_iter()
        .map(|tree_row| {
            let get_value = |node_info: &NodeInfo<&Name, Data>| {
                let node_data = node_info.node_data.into();
                if total == 0 {
                    return 0;
                }
                rounded_div::u64(node_data * (width as u64), total) as usize
            };

            macro_rules! ancestor_value {
                ($index:expr, $fallback:expr) => {
                    tree_row.ancestors.get($index).map_or($fallback, get_value)
                };
            }

            let lv0_value = get_value(&tree_row.node_info);
            let lv1_value = ancestor_value!(3, lv0_value);
            let lv2_value = ancestor_value!(2, lv1_value);
            let lv3_value = ancestor_value!(1, lv2_value);
            let lv4_value = width;
            debug_assert_op!(lv0_value <= lv1_value);
            debug_assert_op!(lv1_value <= lv2_value);
            debug_assert_op!(lv2_value <= lv3_value);
            debug_assert_op!(lv3_value <= lv4_value);

            let lv0_visible = lv0_value;
            let lv1_visible = lv1_value - lv0_value;
            let lv2_visible = lv2_value - lv1_value;
            let lv3_visible = lv3_value - lv2_value;
            let lv4_visible = lv4_value - lv3_value;

            #[cfg(debug_assertions)]
            {
                let actual_lv4_value = ancestor_value!(0, lv3_value);
                if actual_lv4_value != 0 {
                    debug_assert_op!(actual_lv4_value == width);
                    assert_cmp::debug_assert_op_expr!(
                        lv0_visible + lv1_visible + lv2_visible + lv3_visible + lv4_visible,
                        ==,
                        width
                    );
                }
            }

            let proportion_bar = ProportionBar {
                level0: lv0_visible,
                level1: lv1_visible,
                level2: lv2_visible,
                level3: lv3_visible,
                level4: lv4_visible,
            };
            BarRow {
                tree_row,
                proportion_bar,
            }
        })
        .collect()
}