assembly_data/fdb/
io.rs

1//! # General-Purpose file loader
2//!
3//! This is the original entry point to the FDB loading API. A [`SchemaLoader`] wraps
4//! an implementation of [`BufRead`] and loads the data from the file into an
5//! instance of [`Schema`].
6//!
7//! This uses the methods defined in the `reader` module and produces the data
8//! structure defined in the `core` module.
9
10use super::file::{
11    FDBBucketHeader, FDBColumnHeader, FDBFieldData, FDBRowHeader, FDBTableDataHeader,
12    FDBTableDefHeader, FDBTableHeader,
13};
14use super::reader::builder::DatabaseBuilder;
15use super::reader::{DatabaseBufReader, DatabaseReader};
16use super::{common::ValueType, core::*};
17use assembly_core::reader::{FileError, FileResult};
18use std::convert::TryFrom;
19use std::fs;
20use std::io::{BufRead, BufReader, Seek};
21
22/// Configuration for the [`SchemaLoader`]
23pub trait LoaderConfig {
24    /// Whether to process to table specified by `def`
25    fn load_table_data(&self, def: &TableDef) -> bool;
26}
27
28/// Configuration for SchemaLoader
29pub struct LoaderConfigImpl<P>
30where
31    P: Fn(&TableDef) -> bool,
32{
33    /// The policy for tables
34    pub table_data_policy: P,
35}
36
37impl<P> LoaderConfig for LoaderConfigImpl<P>
38where
39    P: Fn(&TableDef) -> bool,
40{
41    fn load_table_data(&self, def: &TableDef) -> bool {
42        (self.table_data_policy)(def)
43    }
44}
45
46/// Structure to load a schema from some encapsulated stream
47pub struct SchemaLoader<'a, T, C> {
48    inner: &'a mut T,
49    config: C,
50}
51
52impl TryFrom<&str> for Schema {
53    type Error = FileError;
54
55    fn try_from(filename: &str) -> FileResult<Schema> {
56        let file = fs::File::open(filename)?;
57        Schema::try_from(file)
58    }
59}
60
61impl TryFrom<fs::File> for Schema {
62    type Error = FileError;
63
64    fn try_from(file: fs::File) -> FileResult<Schema> {
65        let mut reader = BufReader::new(file);
66        let config = LoaderConfigImpl {
67            table_data_policy: |_| true,
68        };
69        let mut loader = SchemaLoader::open(&mut reader, config);
70        loader.try_load_schema()
71    }
72}
73
74impl<'a, T, C> SchemaLoader<'a, T, C>
75where
76    T: BufRead + Seek,
77    C: LoaderConfig,
78{
79    /// Create a new loader from the given reader
80    pub fn open(inner: &'a mut T, config: C) -> Self {
81        Self { inner, config }
82    }
83
84    /// Try to load a row
85    pub fn try_load_row(&mut self, header: FDBRowHeader) -> FileResult<Row> {
86        let a = &mut self.inner;
87        let field_list = a.get_field_data_list(header)?;
88        let field_data: Vec<FDBFieldData> = field_list.into();
89        let mut fields: Vec<Field> = Vec::with_capacity(field_data.len());
90        for field in field_data {
91            match self.inner.try_load_field(&field) {
92                Ok(value) => fields.push(value),
93                Err(e) => println!("{:?}", e),
94            }
95        }
96        Ok(Row::from(fields))
97    }
98
99    /// Try to load a bucket
100    pub fn try_load_bucket(&mut self, header: FDBBucketHeader) -> FileResult<Bucket> {
101        let row_header_addr_it = self
102            .inner
103            .get_row_header_addr_iterator(header.row_header_list_head_addr);
104        let row_header_addr_list = row_header_addr_it.collect::<Result<Vec<_>, _>>()?;
105        let mut rows: Vec<Row> = Vec::with_capacity(row_header_addr_list.len());
106        for row_header_addr in row_header_addr_list {
107            let row_header = self.inner.get_row_header(row_header_addr)?;
108            let row = self.try_load_row(row_header)?;
109            rows.push(row);
110        }
111        Ok(Bucket(rows))
112    }
113
114    /// Try to load a column
115    pub fn try_load_column(&mut self, header: FDBColumnHeader) -> FileResult<Column> {
116        // FIXME: remove unwrap
117        let col_type = ValueType::try_from(header.column_data_type).unwrap();
118        let col_name = self.inner.get_string(header.column_name_addr)?;
119        Ok(Column::from((col_name.as_ref(), col_type)))
120    }
121
122    /// Try to load a table definition
123    pub fn try_load_table_def(&mut self, header: FDBTableDefHeader) -> FileResult<TableDef> {
124        let name = self.inner.get_string(header.table_name_addr)?;
125        let column_header_list: Vec<FDBColumnHeader> =
126            self.inner.get_column_header_list(&header)?.into();
127
128        let columns: Vec<Column> = column_header_list
129            .iter()
130            .map(|column_header| self.try_load_column(*column_header))
131            .collect::<Result<Vec<_>, _>>()?;
132
133        Ok(TableDef { columns, name })
134    }
135
136    /// Try to load table data
137    pub fn try_load_table_data(&mut self, header: FDBTableDataHeader) -> FileResult<TableData> {
138        let bucket_header_list: Vec<FDBBucketHeader> =
139            self.inner.get_bucket_header_list(&header)?.into();
140
141        let buckets: Vec<Bucket> = bucket_header_list
142            .iter()
143            .map(|bucket_header| self.try_load_bucket(*bucket_header))
144            .collect::<Result<Vec<_>, _>>()?;
145
146        Ok(TableData { buckets })
147    }
148
149    /// Try to load a table
150    pub fn try_load_table(&mut self, header: FDBTableHeader) -> FileResult<Table> {
151        let def_header = self
152            .inner
153            .get_table_def_header(header.table_def_header_addr)?;
154        let definition = self.try_load_table_def(def_header)?;
155        if self.config.load_table_data(&definition) {
156            let data_header = self
157                .inner
158                .get_table_data_header(header.table_data_header_addr)?;
159            let data = self.try_load_table_data(data_header)?;
160            Ok(Table::from(definition, data))
161        } else {
162            Ok(Table::new(definition))
163        }
164    }
165
166    /// Try to load a schema
167    pub fn try_load_schema(&mut self) -> FileResult<Schema> {
168        let header = self.inner.get_header()?;
169        let table_header_list: Vec<FDBTableHeader> =
170            self.inner.get_table_header_list(header)?.into();
171        let tables: Vec<Table> = table_header_list
172            .iter()
173            .map(|table_header| self.try_load_table(*table_header))
174            .collect::<Result<Vec<_>, _>>()?;
175        Ok(Schema::from(tables))
176    }
177}