Skip to main content

graphrecords_overview/
graphrecord.rs

1use crate::{GroupOverview, OverviewResult, tabled_modifiers::MergeDuplicatesVerticalByColumn};
2use graphrecords_core::{GraphRecord, prelude::Group};
3use graphrecords_utils::aliases::GrHashMap;
4use std::fmt::{Display, Formatter};
5use tabled::{
6    builder::Builder,
7    settings::{Alignment, Panel, Style, Width, object::Columns, themes::BorderCorrection},
8};
9
10#[derive(Debug, Clone)]
11pub struct Overview {
12    pub ungrouped_overview: GroupOverview,
13    pub grouped_overviews: GrHashMap<Group, GroupOverview>,
14
15    truncate_details: Option<usize>,
16}
17
18impl Display for Overview {
19    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
20        let mut builder = Builder::new();
21
22        builder.push_record([
23            "Group",
24            "Node Count",
25            "Attribute",
26            "Attribute Type",
27            "Data Type",
28            "Details",
29        ]);
30
31        for (group, group_overview) in std::iter::once((None, &self.ungrouped_overview)).chain(
32            self.grouped_overviews
33                .iter()
34                .map(|(group, overview)| (Some(group), overview)),
35        ) {
36            let group_name =
37                group.map_or_else(|| "Ungrouped".to_string(), std::string::ToString::to_string);
38            let count = group_overview.node_overview.count;
39
40            for (attribute, overview) in &group_overview.node_overview.attributes {
41                let details = overview.data.details();
42
43                builder.push_record([
44                    &group_name,
45                    &count.to_string(),
46                    &attribute.to_string(),
47                    overview.data.attribute_type_name(),
48                    &overview.data_type.to_string(),
49                    &details,
50                ]);
51            }
52
53            if group_overview.node_overview.attributes.is_empty() && count > 0 {
54                builder.push_record([&group_name, &count.to_string(), "-", "-", "-", "-"]);
55            }
56        }
57
58        let mut table = builder.build();
59        table.with(Style::modern());
60        table.with(Panel::header("Node Overview"));
61        table.with(MergeDuplicatesVerticalByColumn::new(vec![0, 1]));
62        table.with(Alignment::center_vertical());
63        table.with(BorderCorrection {});
64
65        if let Some(truncate_details) = self.truncate_details {
66            table.modify(Columns::last(), Width::truncate(truncate_details));
67        }
68
69        writeln!(f, "{table}")?;
70
71        let mut builder = Builder::new();
72
73        builder.push_record([
74            "Group",
75            "Edge Count",
76            "Attribute",
77            "Attribute Type",
78            "Data Type",
79            "Details",
80        ]);
81
82        for (group, group_overview) in std::iter::once((None, &self.ungrouped_overview)).chain(
83            self.grouped_overviews
84                .iter()
85                .map(|(group, overview)| (Some(group), overview)),
86        ) {
87            let group_name =
88                group.map_or_else(|| "Ungrouped".to_string(), std::string::ToString::to_string);
89            let count = group_overview.edge_overview.count;
90
91            for (attribute, overview) in &group_overview.edge_overview.attributes {
92                let details = overview.data.details();
93
94                builder.push_record([
95                    &group_name,
96                    &count.to_string(),
97                    &attribute.to_string(),
98                    overview.data.attribute_type_name(),
99                    &overview.data_type.to_string(),
100                    &details,
101                ]);
102            }
103
104            if group_overview.edge_overview.attributes.is_empty() && count > 0 {
105                builder.push_record([&group_name, &count.to_string(), "-", "-", "-", "-"]);
106            }
107        }
108
109        let mut table = builder.build();
110        table.with(Style::modern());
111        table.with(Panel::header("Edge Overview"));
112        table.with(MergeDuplicatesVerticalByColumn::new(vec![0, 1]));
113        table.with(Alignment::center_vertical());
114        table.with(BorderCorrection {});
115
116        if let Some(truncate_details) = self.truncate_details {
117            table.modify(Columns::last(), Width::truncate(truncate_details));
118        }
119
120        writeln!(f, "{table}")
121    }
122}
123
124impl Overview {
125    fn new(graphrecord: &GraphRecord, truncate_details: Option<usize>) -> OverviewResult<Self> {
126        Ok(Self {
127            ungrouped_overview: GroupOverview::new(graphrecord, None, truncate_details)?,
128            grouped_overviews: graphrecord
129                .groups()
130                .map(|group| {
131                    Ok((
132                        group.clone(),
133                        GroupOverview::new(graphrecord, Some(group), truncate_details)?,
134                    ))
135                })
136                .collect::<OverviewResult<_>>()?,
137            truncate_details,
138        })
139    }
140}
141
142pub trait Overviewable {
143    fn overview(&self, truncate_details: Option<usize>) -> OverviewResult<Overview>;
144}
145
146impl Overviewable for GraphRecord {
147    fn overview(&self, truncate_details: Option<usize>) -> OverviewResult<Overview> {
148        Overview::new(self, truncate_details)
149    }
150}