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