realm_db_reader/table/
mod.rs

1mod column;
2mod header;
3mod row;
4
5use anyhow::{Ok, anyhow, bail};
6use log::debug;
7use tracing::instrument;
8
9use crate::array::Array;
10use crate::column::Column;
11pub(crate) use crate::table::column::ColumnAttributes;
12use crate::table::header::TableHeader;
13pub use crate::table::row::Row;
14use crate::value::Value;
15
16/// A view into a single Realm database table.
17#[derive(Debug)]
18#[allow(unused)]
19pub struct Table {
20    header: TableHeader,
21    table_number: usize,
22}
23
24impl Table {
25    /// Construct a new table instance, from the given Realm array.
26    #[instrument(target = "Table", level = "debug")]
27    pub(crate) fn build(array: Array, table_number: usize) -> anyhow::Result<Self> {
28        let header_array = array.get_node(0)?.unwrap();
29        let data_array = array.get_node(1)?.unwrap();
30
31        Self::build_from(&header_array, data_array, table_number)
32    }
33
34    /// Construct a new table instance, from the given Realm arrays for the
35    /// header and data. This is used primarily by subtables, as their header
36    /// and data arrays are in disjointed locations compared to regular tables.
37    #[instrument(target = "Table", level = "debug")]
38    pub(crate) fn build_from(
39        header_array: &Array,
40        data_array: Array,
41        table_number: usize,
42    ) -> anyhow::Result<Self> {
43        let header = TableHeader::build(header_array, &data_array)?;
44
45        let result = Self {
46            header,
47            table_number,
48        };
49
50        debug!(target: "Table", "data: {:?}", result);
51        Ok(result)
52    }
53}
54
55impl Table {
56    /// Get the number of the table, starting with 0, within the
57    /// [`Group`](`crate::group::Group`).
58    ///
59    /// Subtables have a table number of [`usize::MAX`].
60    pub fn get_table_number(&self) -> usize {
61        self.table_number
62    }
63
64    /// Get the column specifications for the table.
65    pub fn get_column_specs(&self) -> &[Box<dyn Column>] {
66        self.header.get_columns()
67    }
68
69    /// Get the specification for the column with the given number (starting with 0).
70    ///
71    /// Returns an error if the column number is out of range.
72    pub fn get_column_spec(&self, column_number: usize) -> Option<&dyn Column> {
73        self.header.get_column(column_number)
74    }
75
76    /// Get the number of rows in the table.
77    #[instrument(target = "Table", level = "debug", skip(self), fields(header = ?self.header))]
78    pub fn row_count(&self) -> anyhow::Result<usize> {
79        let first_column = self
80            .header
81            .get_column(0)
82            .ok_or_else(|| anyhow::anyhow!("No column at index 0: can't load row count"))?;
83        first_column.count()
84    }
85
86    /// Get the row with the given number (starting with 0).
87    #[instrument(target = "Table", level = "debug", skip(self), fields(header = ?self.header))]
88    pub fn get_row<'a>(&'a self, row_number: usize) -> anyhow::Result<Row<'a>> {
89        let values = self.load_row(row_number)?;
90
91        Ok(Row::new(
92            values,
93            self.header
94                .get_columns()
95                .iter()
96                .filter_map(|c| c.name())
97                .map(|n| n.into())
98                .collect(),
99        ))
100    }
101
102    /// Load the values for the row with the given number (starting with 0).
103    #[instrument(target = "Table", level = "debug", skip(self), fields(header = ?self.header))]
104    fn load_row(&self, row_number: usize) -> anyhow::Result<Vec<Value>> {
105        let column_count = self.header.column_count();
106        let mut values = Vec::with_capacity(column_count);
107        for column_number in 0..column_count {
108            log::info!(target: "Table", "loading column {column_number} for row {row_number}");
109            values.push(self.load_column(column_number, row_number)?);
110        }
111
112        Ok(values)
113    }
114
115    /// Determine the row number for the given value in an indexed column.
116    /// Note that if there are multiple rows with the same value, this function
117    /// will return the first one.
118    ///
119    /// Returns an error if there is no column with the given name or if the column is not indexed.
120    ///
121    /// Returns `None` if the value is not found in the indexed column.
122    #[instrument(target = "Table", level = "debug", skip(self), fields(header = ?self.header))]
123    pub fn find_row_number_from_indexed_column(
124        &self,
125        indexed_column_name: &str,
126        value: &Value,
127    ) -> anyhow::Result<Option<usize>> {
128        // Find the column index for the given column name
129        let column_spec = self
130            .header
131            .get_columns()
132            .iter()
133            .find(|col| col.name() == Some(indexed_column_name))
134            .ok_or_else(|| anyhow!("Column not found: {}", indexed_column_name))?;
135
136        if !column_spec.is_indexed() {
137            bail!(
138                "Column '{}' is not indexed, cannot perform lookup",
139                indexed_column_name
140            );
141        }
142
143        column_spec.get_row_number_by_index(value)
144    }
145
146    /// Find and load the row with the given value in an indexed column.
147    /// Note that if there are multiple rows with the same value, only the first one is returned.
148    ///
149    /// Returns an error if there is no column with the given name or if the column is not indexed.
150    ///
151    /// Returns `None` if the value is not found in the indexed column.
152    #[instrument(target = "Table", level = "debug", skip(self), fields(header = ?self.header))]
153    pub fn find_row_from_indexed_column<'a>(
154        &'a self,
155        indexed_column_name: &str,
156        value: &Value,
157    ) -> anyhow::Result<Option<Row<'a>>> {
158        let Some(row_number) =
159            self.find_row_number_from_indexed_column(indexed_column_name, value)?
160        else {
161            return Ok(None);
162        };
163
164        self.get_row(row_number).map(Some)
165    }
166
167    /// Get all rows in the table.
168    #[instrument(target = "Table", level = "debug", skip(self), fields(header = ?self.header))]
169    pub fn get_rows<'a>(&'a self) -> anyhow::Result<Vec<Row<'a>>> {
170        let row_count = self.row_count()?;
171        let mut rows = Vec::with_capacity(row_count);
172
173        for i in 0..row_count {
174            rows.push(self.get_row(i)?);
175        }
176
177        Ok(rows)
178    }
179
180    /// Load the value at the specified column and row.
181    ///
182    /// Panics if the column or row number is out of range.
183    #[instrument(target = "Table", level = "debug", skip(self))]
184    fn load_column(&self, column_number: usize, row_number: usize) -> anyhow::Result<Value> {
185        let column_spec = self
186            .header
187            .get_column(column_number)
188            .unwrap_or_else(|| panic!("Invalid column number {column_number}"));
189        let value = column_spec.get(row_number)?;
190
191        debug!(
192            target: "Table",
193            "Loaded column {column_number} at row {row_number}: {:?}",
194            value
195        );
196
197        Ok(value)
198    }
199}