graphrecords-overview 0.5.0

High-performance graph-based data records
Documentation
use crate::{GroupOverview, OverviewResult, tabled_modifiers::MergeDuplicatesVerticalByColumn};
use graphrecords_core::{GraphRecord, prelude::Group};
use graphrecords_utils::aliases::GrHashMap;
use std::fmt::{Display, Formatter};
use tabled::{
    builder::Builder,
    settings::{Alignment, Panel, Style, Width, object::Columns, themes::BorderCorrection},
};

#[derive(Debug, Clone)]
pub struct Overview {
    pub ungrouped_overview: GroupOverview,
    pub grouped_overviews: GrHashMap<Group, GroupOverview>,

    truncate_details: Option<usize>,
}

impl Display for Overview {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let mut builder = Builder::new();

        builder.push_record([
            "Group",
            "Node Count",
            "Attribute",
            "Attribute Type",
            "Data Type",
            "Details",
        ]);

        for (group, group_overview) in std::iter::once((None, &self.ungrouped_overview)).chain(
            self.grouped_overviews
                .iter()
                .map(|(group, overview)| (Some(group), overview)),
        ) {
            let group_name =
                group.map_or_else(|| "Ungrouped".to_string(), std::string::ToString::to_string);
            let count = group_overview.node_overview.count;

            for (attribute, overview) in &group_overview.node_overview.attributes {
                let details = overview.data.details();

                builder.push_record([
                    &group_name,
                    &count.to_string(),
                    &attribute.to_string(),
                    overview.data.attribute_type_name(),
                    &overview.data_type.to_string(),
                    &details,
                ]);
            }

            if group_overview.node_overview.attributes.is_empty() && count > 0 {
                builder.push_record([&group_name, &count.to_string(), "-", "-", "-", "-"]);
            }
        }

        let mut table = builder.build();
        table.with(Style::modern());
        table.with(Panel::header("Node Overview"));
        table.with(MergeDuplicatesVerticalByColumn::new(vec![0, 1]));
        table.with(Alignment::center_vertical());
        table.with(BorderCorrection {});

        if let Some(truncate_details) = self.truncate_details {
            table.modify(Columns::last(), Width::truncate(truncate_details));
        }

        writeln!(f, "{table}")?;

        let mut builder = Builder::new();

        builder.push_record([
            "Group",
            "Edge Count",
            "Attribute",
            "Attribute Type",
            "Data Type",
            "Details",
        ]);

        for (group, group_overview) in std::iter::once((None, &self.ungrouped_overview)).chain(
            self.grouped_overviews
                .iter()
                .map(|(group, overview)| (Some(group), overview)),
        ) {
            let group_name =
                group.map_or_else(|| "Ungrouped".to_string(), std::string::ToString::to_string);
            let count = group_overview.edge_overview.count;

            for (attribute, overview) in &group_overview.edge_overview.attributes {
                let details = overview.data.details();

                builder.push_record([
                    &group_name,
                    &count.to_string(),
                    &attribute.to_string(),
                    overview.data.attribute_type_name(),
                    &overview.data_type.to_string(),
                    &details,
                ]);
            }

            if group_overview.edge_overview.attributes.is_empty() && count > 0 {
                builder.push_record([&group_name, &count.to_string(), "-", "-", "-", "-"]);
            }
        }

        let mut table = builder.build();
        table.with(Style::modern());
        table.with(Panel::header("Edge Overview"));
        table.with(MergeDuplicatesVerticalByColumn::new(vec![0, 1]));
        table.with(Alignment::center_vertical());
        table.with(BorderCorrection {});

        if let Some(truncate_details) = self.truncate_details {
            table.modify(Columns::last(), Width::truncate(truncate_details));
        }

        writeln!(f, "{table}")
    }
}

impl Overview {
    fn new(graphrecord: &GraphRecord, truncate_details: Option<usize>) -> OverviewResult<Self> {
        Ok(Self {
            ungrouped_overview: GroupOverview::new(graphrecord, None, truncate_details)?,
            grouped_overviews: graphrecord
                .groups()
                .map(|group| {
                    Ok((
                        group.clone(),
                        GroupOverview::new(graphrecord, Some(group), truncate_details)?,
                    ))
                })
                .collect::<OverviewResult<_>>()?,
            truncate_details,
        })
    }
}

pub trait Overviewable {
    fn overview(&self, truncate_details: Option<usize>) -> OverviewResult<Overview>;
}

impl Overviewable for GraphRecord {
    fn overview(&self, truncate_details: Option<usize>) -> OverviewResult<Overview> {
        Overview::new(self, truncate_details)
    }
}