rustronomy_core/universal_containers/table.rs
1/*
2 Copyright© 2022 Raúl Wolters(1)
3
4 This file is part of rustronomy-core.
5
6 rustronomy is free software: you can redistribute it and/or modify it under
7 the terms of the European Union Public License version 1.2 or later, as
8 published by the European Commission.
9
10 rustronomy is distributed in the hope that it will be useful, but WITHOUT ANY
11 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 A PARTICULAR PURPOSE. See the European Union Public License for more details.
13
14 You should have received a copy of the EUPL in an/all official language(s) of
15 the European Union along with rustronomy. If not, see
16 <https://ec.europa.eu/info/european-union-public-licence_en/>.
17
18 (1) Resident of the Kingdom of the Netherlands; agreement between licensor and
19 licensee subject to Dutch law as per article 15 of the EUPL.
20*/
21
22//! This module specifies a table container. Tables consist of named columns
23//! that hold a bunch of values of the same type. Tables should not be used to
24//! store large amounts of data.
25//!
26//! Three column types are supported:
27//! - `Integer` always a `Vec<i64>`
28//! - `Float` always a `Vec<f64>`
29//! - `Text` always a `Vec<String>`
30//!
31//! Columns can be accessed either through their index or through their name, if
32//! one has been supplied.
33
34use std::{
35 fmt::{self, Debug, Display, Formatter},
36 mem,
37};
38
39use indexmap::IndexMap;
40
41#[derive(Debug, Clone)]
42#[non_exhaustive]
43/// columns are the constituent parts of tables. They consist of vectors holding
44/// elements of the same type. Types are differentiated via the variants of this
45/// enum.
46///
47/// Right now, 3 variants are supported:
48/// - `Integer` always a `Vec<i64>`
49/// - `Float` always a `Vec<f64>`
50/// - `Text` always a `Vec<String>`
51/// In the future, more variants may be added as necessary. As such, this enum
52/// is marked as `#[non_exhaustive]`.
53pub enum Col {
54 Integer(Vec<i64>),
55 Float(Vec<f64>),
56 Text(Vec<String>),
57}
58
59impl Col {
60 #[inline]
61 /// returns the number of elements in the column
62 pub fn len(&self) -> usize {
63 use Col::*;
64 match self {
65 Integer(vec) => vec.len(),
66 Float(vec) => vec.len(),
67 Text(vec) => vec.len(),
68 }
69 }
70
71 /// returns the total size of the column in bytes
72 pub fn size(&self) -> usize {
73 //total size = discriminant + usize + the size of the underlying vector
74 mem::size_of::<Self>()
75 + self.len()
76 * match self {
77 Col::Integer(_) => mem::size_of::<i64>(),
78 Col::Float(_) => mem::size_of::<f64>(),
79 Col::Text(vec) => vec.iter().map(|string| string.bytes().len()).sum(),
80 }
81 }
82}
83
84#[derive(Debug, Clone)]
85/// the table data container. Consists of named columns and metadata tags. See
86/// the module-level documentation for more details.
87pub struct Table {
88 //Indexmap to provide easy iteration
89 data: IndexMap<String, Col>,
90}
91
92impl Table {
93 /// creates an empty table without metadata
94 pub fn new() -> Table {
95 Table { data: IndexMap::new() }
96 }
97
98 #[inline]
99 /// adds column to the table and gives it a name, if specified. If a column
100 /// with the same name already exists, it will be overridden.
101 pub fn set_col(&mut self, col_name: &str, col: Col) {
102 self.data.insert(col_name.to_string(), col);
103 }
104
105 #[inline]
106 /// returns reference to the column with the name `col_name` if one exists,
107 /// `None` otherwise.
108 pub fn get_col(&self, col_name: &str) -> Option<&Col> {
109 self.data.get(col_name)
110 }
111
112 #[inline]
113 /// returns mutable reference to the column with the name `col_name` if one
114 /// exists, `None` otherwise.
115 pub fn get_col_mut(&mut self, col_name: &str) -> Option<&mut Col> {
116 self.data.get_mut(col_name)
117 }
118
119 #[inline]
120 /// removes column with the name `col_name` if one exists and returns it.
121 /// Returns `None` if no column named `col_name` exists.
122 pub fn remove_col(&mut self, col_name: &str) -> Option<Col> {
123 self.data.remove(col_name)
124 }
125
126 #[inline]
127 /// returns vec of (column-name, column) pairs, discarding the metadata tags
128 pub fn data(self) -> Vec<(String, Col)> {
129 self.data.into_iter().collect()
130 }
131
132 #[inline]
133 /// returns vec of columns discarding column names and metadata tags
134 pub fn data_unnamed(self) -> Vec<Col> {
135 self.data.into_values().collect()
136 }
137}
138
139impl Display for Table {
140 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
141 #[cfg_attr(rustfmt, rustfmt_skip)]
142 writeln!(f, ">=============================<|RUSTRONOMY TABLE 🦀🌌|>===========================<")?;
143 writeln!(f, "number of colums: {}", self.data.len())?;
144 write!(f, "shape: (")?;
145 for (_name, val) in self.data.iter() {
146 write!(f, "{},", val.len())?;
147 }
148 write!(f, "\u{0008})\n")?;
149 write!(
150 f,
151 "total size: {}",
152 super::fmt_byte_size(self.data.iter().map(|(_name, col)| col.size()).sum())
153 )?;
154 #[cfg_attr(rustfmt, rustfmt_skip)]
155 writeln!(f, ">-----------------------------------<|COLUMNS|>---------------------------------<")?;
156 for (col_name, col) in self.data.iter() {
157 writeln!(f, "[{}]", col_name)?;
158 writeln!(f, " number of elements: {}", col.len())?;
159 writeln!(f, " size: {}", super::fmt_byte_size(col.size()))?;
160 }
161 #[cfg_attr(rustfmt, rustfmt_skip)]
162 writeln!(f, ">==============================================================================<")
163 }
164}