Skip to main content

step_p21/
tables.rs

1//! Handling "exchange structure graph" as tables
2//!
3//! Since records in an exchange structure has references to other records,
4//! then consists a graph.
5//!
6//! - An exchange structure corresponds to a graph, we call it "exchange
7//!   structure graph" here.
8//! - A node of graph corresponds to a record.
9//! - An edge of graph corresponds to a reference in a record.
10//!
11//! Creating table from exchange structure AST
12//! -------------------------------------------
13//! Let us consider a simple EXPRESS schema:
14//!
15//! ```text
16//! ENTITY a;
17//!   x: INTEGER;
18//!   y: INTEGER;
19//! END_ENTITY;
20//!
21//! ENTITY b;
22//!   z: INTEGER;
23//!   w: a;
24//! END_ENTITY;
25//! ```
26//!
27//! Corresponding data section in STEP file will be something like following
28//! (skip HEADER section):
29//!
30//! ```text
31//! DATA;
32//!   #1 = A(1, 2);
33//!   #2 = A(3, 4);
34//!   #3 = B(5, #1);
35//!   #4 = B(6, #1);
36//!   #5 = B(7, #2);
37//!   #6 = B(8, A((9, 10)));
38//! ENDSEC;
39//! ```
40//!
41//! In this example, `#3` and `#4` has reference to `#1`.
42//! There will exist non-exclusive reference between entity instances generally,
43//! and thus the data must be regarded as a graph.
44//!
45//! step_p21 will parse this data section into following tables:
46//!
47//! | Table (a) | x (i64) | y (i64) |
48//! |:----------|:--------|:--------|
49//! | `#1`      | 1       | 2       |
50//! | `#2`      | 3       | 4       |
51//!
52//! | Table (b) | z (i64) | w (a) |
53//! |:----------|:--------|:------|
54//! | `#3`      | 5       | `#1`  |
55//! | `#4`      | 6       | `#1`  |
56//! | `#5`      | 7       | `#2`  |
57//! | `#6`      | 8       | `A((9, 10))` |
58//!
59//! Each columns are defined by EXPRESS schema.
60//! `x`, `y`, and `z` are specified as integer in EXPRESS, and will be treated
61//! as `i64` in Rust code. The simple types in EXPRESS are mapped into Rust
62//! primitive types. The ENTITY `a` will be treated as a Rust struct like
63//!
64//! ```
65//! struct A {
66//!     x: i64,
67//!     y: i64,
68//! }
69//! ```
70//!
71//! The ENTITY `b` has to support both reference and inline struct like as `#4`
72//! and `#6`. For this purpose, [PlaceHolder] exists:
73//!
74//! ```
75//! # use step_p21::ast::Name;
76//! enum PlaceHolder<T> {
77//!     /// For reference, e.g. `#1`
78//!     Ref(Name),
79//!     /// For inline typed parameter, e.g. `A((9, 10))`
80//!     Owned(T),
81//! }
82//! ```
83//!
84//! Then following two Rust structs will be defined:
85//!
86//! ```
87//! # use step_p21::tables::PlaceHolder;
88//! # struct A {}
89//! # struct AHolder {}
90//! struct B {
91//!     z: i64,
92//!     w: A,
93//! }
94//! struct BHolder {
95//!     z: i64,
96//!     w: PlaceHolder<AHolder>,
97//! }
98//! ```
99//!
100//! There also a function [IntoOwned::into_owned] to convert a holder struct
101//! `BHolder` into owned struct `B`.
102//! `AHolder` will also be introduced to keep consistency.
103//! These are automated by [step_p21_derive::Holder] proc-macro.
104
105use crate::{ast::*, error::*};
106use serde::{
107    Deserialize,
108    de::{self, IntoDeserializer, VariantAccess},
109};
110use std::{collections::HashMap, fmt, marker::PhantomData};
111
112/// Trait for resolving a reference through entity id
113pub trait IntoOwned: Clone + 'static {
114    type Owned;
115    type Table;
116    fn into_owned(self, table: &Self::Table) -> Result<Self::Owned>;
117}
118
119impl<T: IntoOwned> IntoOwned for Vec<T> {
120    type Owned = Vec<T::Owned>;
121    type Table = T::Table;
122
123    fn into_owned(self, table: &Self::Table) -> Result<Self::Owned> {
124        self.into_iter().map(|x| x.into_owned(table)).collect()
125    }
126}
127
128/// Trait for a field of tables
129pub trait Holder: IntoOwned {
130    fn name() -> &'static str;
131    fn attr_len() -> usize;
132}
133
134pub trait WithVisitor {
135    type Visitor: for<'de> de::Visitor<'de, Value = Self>;
136    fn visitor_new() -> Self::Visitor;
137}
138
139/// Trait for tables which pulls an entity (`T`) from an entity id (`u64`)
140pub trait EntityTable<T: Holder<Table = Self>> {
141    /// Get owned entity from table
142    fn get_owned(&self, entity_id: u64) -> Result<T::Owned>;
143
144    /// Get owned entities as an iterator
145    fn owned_iter<'table>(
146        &'table self,
147    ) -> Box<dyn Iterator<Item = Result<T::Owned>> + 'table>;
148}
149
150/// Create Table from [DataSection]
151pub trait TableInit: Default {
152    fn append_data_section(&mut self, section: &DataSection) -> Result<()>;
153
154    fn from_data_section(section: &DataSection) -> Result<Self> {
155        let mut table = Self::default();
156        table.append_data_section(section)?;
157        Ok(table)
158    }
159
160    fn from_data_sections(sections: &[DataSection]) -> Result<Self> {
161        let mut table = Self::default();
162        for section in sections {
163            table.append_data_section(section)?;
164        }
165        Ok(table)
166    }
167}
168
169pub fn get_owned<T, Table>(
170    table: &Table,
171    map: &HashMap<u64, T>,
172    entity_id: u64,
173) -> Result<T::Owned>
174where
175    T: Holder<Table = Table>,
176    Table: EntityTable<T>,
177{
178    match map.get(&entity_id) {
179        Some(holder) => holder.clone().into_owned(table),
180        None => Err(Error::UnknownEntity(entity_id)),
181    }
182}
183
184pub fn owned_iter<'table, T, Table>(
185    table: &'table Table,
186    map: &'table HashMap<u64, T>,
187) -> Box<dyn Iterator<Item = Result<T::Owned>> + 'table>
188where
189    T: Holder<Table = Table>,
190    Table: EntityTable<T>,
191{
192    Box::new(
193        map.values()
194            .cloned()
195            .map(move |value| value.into_owned(table)),
196    )
197}
198
199/// Helper function to implement TableInit trait
200pub fn insert_record<'de, T: de::Deserialize<'de>>(
201    table: &mut HashMap<u64, T>,
202    id: u64,
203    record: &Record,
204) -> crate::error::Result<()> {
205    if table
206        .insert(id, de::Deserialize::deserialize(record)?)
207        .is_some()
208    {
209        Err(Error::DuplicatedEntity(id))
210    } else {
211        Ok(())
212    }
213}
214
215/// Owned value or reference through entity/value id
216#[derive(Debug, Clone, PartialEq)]
217pub enum PlaceHolder<T> {
218    Ref(Name),
219    Owned(T),
220}
221
222impl<T: Holder> IntoOwned for PlaceHolder<T>
223where
224    T::Table: EntityTable<T>,
225{
226    type Owned = T::Owned;
227    type Table = T::Table;
228
229    /// Get owned value, or look up entity table and clone it for a reference.
230    ///
231    /// Errors
232    /// -------
233    /// - if table lookup failed, i.e. unknown entity id not registered in the
234    ///   table
235    fn into_owned(self, table: &Self::Table) -> Result<T::Owned> {
236        match self {
237            PlaceHolder::Ref(id) => match id {
238                Name::Entity(id) => table.get_owned(id),
239                _ => unimplemented!("ENTITY is only supported now"),
240            },
241            PlaceHolder::Owned(a) => a.into_owned(table),
242        }
243    }
244}
245
246impl<T: Holder> From<T> for PlaceHolder<T> {
247    fn from(owned: T) -> Self {
248        PlaceHolder::Owned(owned)
249    }
250}
251
252impl<T> From<Name> for PlaceHolder<T> {
253    fn from(rvalue: Name) -> Self {
254        PlaceHolder::Ref(rvalue)
255    }
256}
257
258impl<'de, T: Holder + WithVisitor + Deserialize<'de>> Deserialize<'de>
259    for PlaceHolder<T>
260{
261    fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
262    where
263        D: de::Deserializer<'de>,
264    {
265        deserializer.deserialize_tuple_struct(
266            T::name(),
267            T::attr_len(),
268            PlaceHolderVisitor::<T>::default(),
269        )
270    }
271}
272
273struct PlaceHolderVisitor<T> {
274    phantom: PhantomData<T>,
275}
276
277impl<T> Default for PlaceHolderVisitor<T> {
278    fn default() -> Self {
279        PlaceHolderVisitor {
280            phantom: PhantomData,
281        }
282    }
283}
284
285impl<'de, T: Deserialize<'de> + Holder + WithVisitor> de::Visitor<'de>
286    for PlaceHolderVisitor<T>
287{
288    type Value = PlaceHolder<T>;
289
290    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
291        write!(formatter, "PlaceHolder<{}>", std::any::type_name::<T>())
292    }
293
294    fn visit_i64<E>(self, v: i64) -> ::std::result::Result<Self::Value, E>
295    where
296        E: de::Error,
297    {
298        Ok(PlaceHolder::Owned(T::deserialize(v.into_deserializer())?))
299    }
300
301    fn visit_f64<E>(self, v: f64) -> ::std::result::Result<Self::Value, E>
302    where
303        E: de::Error,
304    {
305        Ok(PlaceHolder::Owned(T::deserialize(v.into_deserializer())?))
306    }
307
308    fn visit_str<E>(self, v: &str) -> ::std::result::Result<Self::Value, E>
309    where
310        E: de::Error,
311    {
312        Ok(PlaceHolder::Owned(T::deserialize(v.into_deserializer())?))
313    }
314
315    fn visit_seq<A>(
316        self,
317        seq: A,
318    ) -> ::std::result::Result<Self::Value, A::Error>
319    where
320        A: de::SeqAccess<'de>,
321    {
322        let visitor = T::visitor_new();
323        Ok(PlaceHolder::Owned(visitor.visit_seq(seq)?))
324    }
325
326    // For Ref(Name)
327    fn visit_enum<A>(
328        self,
329        data: A,
330    ) -> ::std::result::Result<Self::Value, A::Error>
331    where
332        A: de::EnumAccess<'de>,
333    {
334        let (key, variant): (String, _) = data.variant()?;
335        match key.as_str() {
336            "Entity" => {
337                let value: u64 = variant.newtype_variant()?;
338                Ok(PlaceHolder::Ref(Name::Entity(value)))
339            }
340            "Value" => {
341                let value: u64 = variant.newtype_variant()?;
342                Ok(PlaceHolder::Ref(Name::Value(value)))
343            }
344            "ConstantEntity" => {
345                let name: String = variant.newtype_variant()?;
346                Ok(PlaceHolder::Ref(Name::ConstantEntity(name)))
347            }
348            "ConstantValue" => {
349                let name: String = variant.newtype_variant()?;
350                Ok(PlaceHolder::Ref(Name::ConstantValue(name)))
351            }
352            _ => unreachable!("Invalid key while deserializing PlaceHolder"),
353        }
354    }
355
356    // Entry point for Record or Parameter::Typed
357    fn visit_map<A>(
358        self,
359        map: A,
360    ) -> ::std::result::Result<Self::Value, A::Error>
361    where
362        A: de::MapAccess<'de>,
363    {
364        let visitor = T::visitor_new();
365        Ok(PlaceHolder::Owned(visitor.visit_map(map)?))
366    }
367}