graphrecords_overview/
graphrecord.rs1use 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}