realm_db_reader/table/
mod.rs

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