Skip to main content

ifc_tabular/read/
mod.rs

1//! Borrowed read views over tables and time series.
2//!
3//! A read borrows the model and returns what the file says, including
4//! what it says wrongly: every malformed slot, dangling or mistyped
5//! reference, and WR1/WR2 violation is reported as a [`TabularIssue`]
6//! beside whatever did decode, never silently dropped.
7//!
8//! # Declared schema
9//!
10//! Slots are located by attribute name in the schema the caller passes,
11//! so arity and positions come from IFC4 ADD2 TC1 or IFC4X3 ADD2 exactly
12//! as declared. IFC2x3 is refused rather than approximated: its
13//! `IfcTimeSeries` times are `IfcDateTimeSelect` references, not the
14//! `IfcDateTime` strings this view decodes, and its `IfcTable` has no
15//! `Columns`.
16
17mod decode;
18mod issue;
19mod series;
20mod table;
21
22use ifc_model::{EntityId, Model};
23use ifc_schema::{Schema, SchemaVersion};
24
25use crate::error::{TabularReadError, TabularReadResult};
26
27pub use issue::TabularIssue;
28pub use series::{SeriesValue, TimeSeries, TimeSeriesKind};
29pub use table::{Table, TableColumn, TableRow};
30
31/// Borrowed entry point for reading tables and time series.
32#[derive(Debug, Clone, Copy)]
33pub struct TabularView<'m> {
34    model: &'m Model,
35    schema: &'m Schema,
36}
37
38impl<'m> TabularView<'m> {
39    /// Read `model` under the declared `schema`.
40    ///
41    /// # Errors
42    ///
43    /// Refuses a schema other than IFC4 or IFC4X3.
44    pub fn new(model: &'m Model, schema: &'m Schema) -> TabularReadResult<Self> {
45        match schema.version() {
46            Some(SchemaVersion::Ifc4 | SchemaVersion::Ifc4x3) => Ok(Self { model, schema }),
47            _ => Err(TabularReadError::UnsupportedSchema {
48                schema: schema.name().to_owned(),
49            }),
50        }
51    }
52
53    /// Ids of every `IfcTable` in the model.
54    #[must_use]
55    pub fn table_ids(self) -> &'m [EntityId] {
56        self.model.ids_of_type(table::TABLE)
57    }
58
59    /// Ids of every regular, then every irregular, time series.
60    #[must_use]
61    pub fn series_ids(self) -> Vec<EntityId> {
62        let mut ids = self.model.ids_of_type(series::REGULAR).to_vec();
63        ids.extend_from_slice(self.model.ids_of_type(series::IRREGULAR));
64        ids
65    }
66
67    /// Read the `IfcTable` at `id`.
68    ///
69    /// # Errors
70    ///
71    /// Refuses an absent id and a record of another type. Defects inside
72    /// the table are not errors; they are in [`Table::issues`].
73    pub fn table(self, id: EntityId) -> TabularReadResult<Table<'m>> {
74        let entity = self
75            .model
76            .get(id)
77            .ok_or(TabularReadError::UnknownEntity { id })?;
78        if !entity.is_type(table::TABLE) {
79            return Err(TabularReadError::WrongEntityType {
80                id,
81                expected: "IFCTABLE",
82                actual: entity.type_name.to_string(),
83            });
84        }
85        Ok(table::read_table(self.model, self.schema, id, entity))
86    }
87
88    /// Read the regular or irregular time series at `id`.
89    ///
90    /// # Errors
91    ///
92    /// Refuses an absent id and a record that is neither subtype. Defects
93    /// inside the series are in [`TimeSeries::issues`].
94    pub fn time_series(self, id: EntityId) -> TabularReadResult<TimeSeries<'m>> {
95        let entity = self
96            .model
97            .get(id)
98            .ok_or(TabularReadError::UnknownEntity { id })?;
99        let type_name = if entity.is_type(series::REGULAR) {
100            series::REGULAR
101        } else if entity.is_type(series::IRREGULAR) {
102            series::IRREGULAR
103        } else {
104            return Err(TabularReadError::WrongEntityType {
105                id,
106                expected: "IFCREGULARTIMESERIES or IFCIRREGULARTIMESERIES",
107                actual: entity.type_name.to_string(),
108            });
109        };
110        Ok(series::read_series(
111            self.model,
112            self.schema,
113            id,
114            entity,
115            type_name,
116        ))
117    }
118}