ifc_lite_landxml/stream/
event.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Public, renderer-independent stream records.
6
7use crate::{
8    alignment::{
9        LandXmlAlignment as LandXmlHorizontalAlignment, LandXmlAlignmentDocument,
10        LandXmlAlignmentRenderData,
11    },
12    LandXmlAlignment as LandXmlTerrainAlignment, LandXmlCapabilityDiagnostic, LandXmlCgPoint,
13    LandXmlCrossSection, LandXmlCrossSectionSurface, LandXmlExtension, LandXmlMonument,
14    LandXmlParcel, LandXmlPipeFeature, LandXmlPipeNetwork, LandXmlPipeNetworkCollection,
15    LandXmlPipeNetworkDocument, LandXmlPipeRefusal, LandXmlPlanDocument, LandXmlPlanFeature,
16    LandXmlPlanPoint, LandXmlPlanSourceBatch, LandXmlPreservedOnlyExtension, LandXmlProfile,
17    LandXmlRoadway, LandXmlTinDocument, LandXmlUnits,
18};
19use serde::{Deserialize, Serialize};
20
21#[derive(Clone, Debug, Serialize)]
22pub struct LandXmlStreamHeader {
23    pub version: String,
24    /// Units are absent for legal preserved-only source documents. Renderable
25    /// numeric surfaces still require them before a surface is emitted.
26    pub units: Option<LandXmlUnits>,
27}
28
29#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
30#[serde(rename_all = "snake_case")]
31pub enum LandXmlSurfaceComponent {
32    Start,
33    Points,
34    CanonicalVertices,
35    SourceDataPoints,
36    Faces,
37    Boundaries,
38    Breaklines,
39    Contours,
40    End,
41}
42
43#[derive(Clone, Debug, Serialize)]
44pub struct LandXmlSurfaceFragment {
45    pub source_id: String,
46    pub component: LandXmlSurfaceComponent,
47    pub sequence: usize,
48    pub continued: bool,
49    pub payload_utf8: Vec<u8>,
50}
51
52#[derive(Clone, Debug, Serialize)]
53#[serde(tag = "kind", rename_all = "snake_case")]
54pub enum LandXmlStreamEvent {
55    Header(LandXmlStreamHeader),
56    Surface(LandXmlSurfaceFragment),
57    /// Metadata can contain a complete semantic record. Keep it separately
58    /// owned so lightweight surface/header transport records stay compact.
59    Metadata(Box<LandXmlMetadataStreamEvent>),
60}
61
62/// Metadata header emitted once, after all surface fragments have received
63/// credit. Its record vectors are intentionally empty; their values follow as
64/// individually owned [`LandXmlMetadataStreamEvent::Record`] values.
65#[derive(Clone, Debug, Serialize)]
66pub struct LandXmlMetadataStreamHeader {
67    pub stream: LandXmlStreamHeader,
68    pub terrain: LandXmlTinDocument,
69    pub plan: LandXmlPlanDocument,
70    pub alignments: LandXmlAlignmentDocument,
71    pub pipe_networks: LandXmlPipeNetworkDocument,
72}
73
74/// One complete top-level LandXML semantic record. A consumer can release the
75/// record before requesting the next credited event.
76#[derive(Clone, Debug, Deserialize, Serialize)]
77#[serde(tag = "record", content = "value", rename_all = "snake_case")]
78pub enum LandXmlMetadataRecord {
79    TerrainExtension(LandXmlExtension),
80    TerrainWarning(String),
81    TerrainAlignment(LandXmlTerrainAlignment),
82    TerrainProfile(LandXmlProfile),
83    TerrainCrossSection(LandXmlCrossSection),
84    TerrainCrossSectionSurface(LandXmlCrossSectionSurface),
85    TerrainRoadway(LandXmlRoadway),
86    TerrainCapabilityDiagnostic(LandXmlCapabilityDiagnostic),
87    TerrainPreservedOnlyExtension(LandXmlPreservedOnlyExtension),
88    PlanCogoPoint(LandXmlCgPoint),
89    PlanMonument(LandXmlMonument),
90    PlanFeature(LandXmlPlanFeature),
91    PlanParcel(LandXmlParcel),
92    PlanWarning(String),
93    /// Renderer-independent derived plan partitions.  These are ordinary
94    /// credited records rather than an unbounded compatibility payload on End.
95    PlanSourceBatch(LandXmlPlanSourceBatch),
96    PlanParcelProbe(LandXmlPlanParcelProbe),
97    PlanResolvedMonument(LandXmlPlanResolvedMonument),
98    PlanResolvedGeometry(LandXmlPlanResolvedGeometry),
99    AlignmentRenderSpan(crate::alignment::LandXmlAlignmentRenderSpan),
100    AlignmentRenderRefusal(crate::alignment::LandXmlAlignmentRenderRefusal),
101    AlignmentRenderTruncated(bool),
102    HorizontalAlignment(LandXmlHorizontalAlignment),
103    HorizontalAlignmentWarning(String),
104    PipeCollection(LandXmlPipeNetworkCollection),
105    PipeFeature(LandXmlPipeFeature),
106    /// Cursor-only refusal probe emitted directly before its retained network.
107    /// It never enters the reassembled semantic document.
108    PipePreflightRefusal(LandXmlPipeRefusal),
109    PipeNetwork(LandXmlPipeNetwork),
110    PipeRefusal(LandXmlPipeRefusal),
111}
112
113/// One parcel probe paired with the authored parcel provenance it describes.
114#[derive(Clone, Debug, Deserialize, Serialize)]
115pub struct LandXmlPlanParcelProbe {
116    pub source_id: crate::LandXmlSourceId,
117    #[serde(flatten)]
118    pub probe: crate::LandXmlParcelProbe,
119}
120
121/// A single monument's canonical resolved position, if one exists.
122#[derive(Clone, Debug, Deserialize, Serialize)]
123pub struct LandXmlPlanResolvedMonument {
124    pub source_id: crate::LandXmlSourceId,
125    pub point: Option<LandXmlPlanPoint>,
126}
127
128/// Canonical endpoint resolution for one authored plan geometry record.
129#[derive(Clone, Debug, Deserialize, Serialize)]
130pub struct LandXmlPlanResolvedGeometry {
131    pub source_id: crate::LandXmlSourceId,
132    pub start: Option<LandXmlPlanPoint>,
133    pub end: Option<LandXmlPlanPoint>,
134    pub center: Option<LandXmlPlanPoint>,
135    pub pi: Option<LandXmlPlanPoint>,
136}
137
138/// Counters emitted after every move-owned metadata record.
139#[derive(Clone, Debug, Serialize)]
140pub struct LandXmlMetadataStreamEnd {
141    pub surfaces_drained: usize,
142    pub renderable_surfaces: usize,
143    pub preserved_surfaces: usize,
144    pub plan_cogo_points: usize,
145    pub plan_parcels: usize,
146    pub horizontal_alignments: usize,
147    /// Whether the source declared at least one PipeNetwork, including a
148    /// network retained only as a semantic refusal.
149    pub has_pipe_networks: bool,
150    pub pipe_networks: usize,
151    pub pipe_structures: usize,
152    pub pipes: usize,
153    pub pipe_refusals: usize,
154}
155
156/// Resumable metadata events delivered through [`LandXmlStreamEvent::Metadata`].
157#[derive(Clone, Debug, Serialize)]
158#[serde(tag = "metadata_kind", rename_all = "snake_case")]
159pub enum LandXmlMetadataStreamEvent {
160    /// The header contains the bounded family roots; keep record and fragment
161    /// events compact while the header is awaiting credited delivery.
162    Header(Box<LandXmlMetadataStreamHeader>),
163    /// Records may carry a full semantic source item. The cursor moves that
164    /// item through the credited queue without inflating every other event.
165    Record(Box<LandXmlMetadataRecord>),
166    /// A single legal metadata value which is too large for one credited
167    /// transport event. The receiver must concatenate `payload_utf8` in
168    /// sequence order, decode it as JSON, and handle it exactly as the
169    /// corresponding [`Self::Record`] value.
170    RecordFragment(LandXmlMetadataRecordFragment),
171    End(LandXmlMetadataStreamEnd),
172}
173
174/// Bounded transport fragment for one metadata record value.
175#[derive(Clone, Debug, Serialize)]
176pub struct LandXmlMetadataRecordFragment {
177    pub record: String,
178    pub sequence: usize,
179    pub continued: bool,
180    pub payload_utf8: Vec<u8>,
181}
182
183impl LandXmlMetadataRecord {
184    pub(crate) fn wire_name(&self) -> &'static str {
185        match self {
186            Self::TerrainExtension(_) => "terrain_extension",
187            Self::TerrainWarning(_) => "terrain_warning",
188            Self::TerrainAlignment(_) => "terrain_alignment",
189            Self::TerrainProfile(_) => "terrain_profile",
190            Self::TerrainCrossSection(_) => "terrain_cross_section",
191            Self::TerrainCrossSectionSurface(_) => "terrain_cross_section_surface",
192            Self::TerrainRoadway(_) => "terrain_roadway",
193            Self::TerrainCapabilityDiagnostic(_) => "terrain_capability_diagnostic",
194            Self::TerrainPreservedOnlyExtension(_) => "terrain_preserved_only_extension",
195            Self::PlanCogoPoint(_) => "plan_cogo_point",
196            Self::PlanMonument(_) => "plan_monument",
197            Self::PlanFeature(_) => "plan_feature",
198            Self::PlanParcel(_) => "plan_parcel",
199            Self::PlanWarning(_) => "plan_warning",
200            Self::PlanSourceBatch(_) => "plan_source_batch",
201            Self::PlanParcelProbe(_) => "plan_parcel_probe",
202            Self::PlanResolvedMonument(_) => "plan_resolved_monument",
203            Self::PlanResolvedGeometry(_) => "plan_resolved_geometry",
204            Self::AlignmentRenderSpan(_) => "alignment_render_span",
205            Self::AlignmentRenderRefusal(_) => "alignment_render_refusal",
206            Self::AlignmentRenderTruncated(_) => "alignment_render_truncated",
207            Self::HorizontalAlignment(_) => "horizontal_alignment",
208            Self::HorizontalAlignmentWarning(_) => "horizontal_alignment_warning",
209            Self::PipeCollection(_) => "pipe_collection",
210            Self::PipeFeature(_) => "pipe_feature",
211            Self::PipePreflightRefusal(_) => "pipe_preflight_refusal",
212            Self::PipeNetwork(_) => "pipe_network",
213            Self::PipeRefusal(_) => "pipe_refusal",
214        }
215    }
216
217    pub(crate) fn serialize_value(&self) -> Result<Vec<u8>, serde_json::Error> {
218        match self {
219            Self::TerrainExtension(value) => serde_json::to_vec(value),
220            Self::TerrainWarning(value) => serde_json::to_vec(value),
221            Self::TerrainAlignment(value) => serde_json::to_vec(value),
222            Self::TerrainProfile(value) => serde_json::to_vec(value),
223            Self::TerrainCrossSection(value) => serde_json::to_vec(value),
224            Self::TerrainCrossSectionSurface(value) => serde_json::to_vec(value),
225            Self::TerrainRoadway(value) => serde_json::to_vec(value),
226            Self::TerrainCapabilityDiagnostic(value) => serde_json::to_vec(value),
227            Self::TerrainPreservedOnlyExtension(value) => serde_json::to_vec(value),
228            Self::PlanCogoPoint(value) => serde_json::to_vec(value),
229            Self::PlanMonument(value) => serde_json::to_vec(value),
230            Self::PlanFeature(value) => serde_json::to_vec(value),
231            Self::PlanParcel(value) => serde_json::to_vec(value),
232            Self::PlanWarning(value) => serde_json::to_vec(value),
233            Self::PlanSourceBatch(value) => serde_json::to_vec(value),
234            Self::PlanParcelProbe(value) => serde_json::to_vec(value),
235            Self::PlanResolvedMonument(value) => serde_json::to_vec(value),
236            Self::PlanResolvedGeometry(value) => serde_json::to_vec(value),
237            Self::AlignmentRenderSpan(value) => serde_json::to_vec(value),
238            Self::AlignmentRenderRefusal(value) => serde_json::to_vec(value),
239            Self::AlignmentRenderTruncated(value) => serde_json::to_vec(value),
240            Self::HorizontalAlignment(value) => serde_json::to_vec(value),
241            Self::HorizontalAlignmentWarning(value) => serde_json::to_vec(value),
242            Self::PipeCollection(value) => serde_json::to_vec(value),
243            Self::PipeFeature(value) => serde_json::to_vec(value),
244            Self::PipePreflightRefusal(value) => serde_json::to_vec(value),
245            Self::PipeNetwork(value) => serde_json::to_vec(value),
246            Self::PipeRefusal(value) => serde_json::to_vec(value),
247        }
248    }
249}
250
251/// Complete non-surface semantics finalized from the same event-driven
252/// parsers. `terrain.surfaces` is empty because complete surfaces were already
253/// emitted as bounded fragments with stable source identities.
254#[derive(Clone, Debug, Serialize)]
255pub struct LandXmlStreamMetadata {
256    pub terrain: LandXmlTinDocument,
257    pub plan: LandXmlPlanDocument,
258    pub alignments: LandXmlAlignmentDocument,
259    pub alignment_render: LandXmlAlignmentRenderData,
260}
261
262/// Deliberately not a fake complete document: surface data is drained before
263/// finalization and the summary owns only bounded counters and header facts.
264#[derive(Clone, Debug, Serialize)]
265pub struct LandXmlStreamSummary {
266    pub header: LandXmlStreamHeader,
267    pub surfaces_drained: usize,
268    pub renderable_surfaces: usize,
269    pub preserved_surfaces: usize,
270    pub plan_cogo_points: usize,
271    pub plan_parcels: usize,
272    pub horizontal_alignments: usize,
273    /// Pipe-family totals are finalized from the same canonical event stream;
274    /// the stream deliberately does not retain complete pipe documents.
275    pub pipe_networks: usize,
276    pub pipe_structures: usize,
277    pub pipes: usize,
278    pub pipe_refusals: usize,
279    pub metadata: LandXmlStreamMetadata,
280}