Skip to main content

ifc_alignment/authoring/
mod.rs

1//! Author IFC4X3 alignment entities from plain numbers.
2//!
3//! Mirrors ADR 0011: this is the write direction of the same bridge that
4//! reads alignments, and it indexes `crate::slot` rather than restating a
5//! single attribute position.
6//!
7//! Authoring records is independent of 3D composition. A file states its
8//! horizontal layout, its vertical profile and its cant as separate
9//! parameter segments; composing them into a centreline is a lowering
10//! concern and does not gate writing the records.
11//!
12//! Values are plain `f64` in the units the caller declares in the file.
13//! Nothing here converts units: the readers apply `AlignmentUnits` on the
14//! way out, so applying a factor here too would double-scale.
15
16mod layout;
17mod linear;
18mod referent;
19mod segment;
20
21pub use layout::{alignment, alignment_segment, cant_layout, horizontal_layout, vertical_layout};
22pub use linear::{linear_element, linear_positioning_element};
23pub use referent::{
24    axis2_placement_linear, cartesian_point, linear_placement, point_by_distance, referent,
25    stationing,
26};
27pub use segment::{
28    cant_segment, horizontal_segment, vertical_segment, CantSegmentDraft, HorizontalSegmentDraft,
29    VerticalSegmentDraft,
30};
31
32use ifc_model::Value;
33
34use crate::error::AlignmentError;
35
36/// Refuse an authored value before anything is staged.
37pub(crate) fn invalid(
38    type_name: &'static str,
39    attribute: &'static str,
40    detail: impl Into<String>,
41) -> AlignmentError {
42    AlignmentError::InvalidAuthoredValue {
43        type_name,
44        attribute,
45        detail: detail.into(),
46    }
47}
48
49/// Refuse a non-finite measure. NaN and infinity parse back as garbage
50/// rather than failing, so they are rejected at the boundary.
51pub(crate) fn finite(
52    type_name: &'static str,
53    attribute: &'static str,
54    value: f64,
55) -> Result<(), AlignmentError> {
56    if value.is_finite() {
57        Ok(())
58    } else {
59        Err(invalid(
60            type_name,
61            attribute,
62            format!("{value} is not finite"),
63        ))
64    }
65}
66
67/// A 22-character IFC GUID, checked for length only -- the alphabet is the
68/// caller's business, but a wrong length is always malformed.
69pub(crate) fn guid(type_name: &'static str, value: &str) -> Result<Value, AlignmentError> {
70    if value.chars().count() != 22 {
71        return Err(invalid(
72            type_name,
73            "GlobalId",
74            format!("expected 22 characters, got {}", value.chars().count()),
75        ));
76    }
77    Ok(Value::Text(value.into()))
78}