ifc_lite_landxml/alignment/
render_data.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//! Bounded display samples derived from already-parsed alignment semantics.
6
7use super::{LandXmlAlignmentDocument, LandXmlAlignmentRenderSpan};
8
9const POINTS_PER_SPAN: usize = 65;
10const MAX_POINTS: usize = 250_000;
11const MAX_REFUSALS: usize = 1_024;
12
13/// One numeric display-sampling refusal retained with its authored identity.
14#[derive(Clone, Debug, serde::Deserialize, PartialEq, serde::Serialize)]
15pub struct LandXmlAlignmentRenderRefusal {
16    pub source_id: String,
17    pub message: String,
18}
19
20/// Bounded derived display data. It is never a substitute for the source
21/// alignment document or its exact probe API.
22#[derive(Clone, Debug, PartialEq, serde::Serialize)]
23pub struct LandXmlAlignmentRenderData {
24    pub spans: Vec<LandXmlAlignmentRenderSpan>,
25    pub refusals: Vec<LandXmlAlignmentRenderRefusal>,
26    pub truncated: bool,
27}
28
29/// Sample only the supported analytic primitives from an existing source
30/// document. This does not parse XML and so is safe after stream finalization.
31pub fn alignment_render_data(document: &LandXmlAlignmentDocument) -> LandXmlAlignmentRenderData {
32    let mut spans = Vec::new();
33    let mut refusals = Vec::new();
34    let mut point_count = 0usize;
35    let mut truncated = false;
36    for alignment in &document.alignments {
37        for segment in &alignment.segments {
38            match segment.render_span(POINTS_PER_SPAN) {
39                Ok(Some(span)) => {
40                    let Some(next_point_count) = point_count.checked_add(span.points.len()) else {
41                        return LandXmlAlignmentRenderData {
42                            spans,
43                            refusals,
44                            truncated: true,
45                        };
46                    };
47                    if next_point_count > MAX_POINTS {
48                        return LandXmlAlignmentRenderData {
49                            spans,
50                            refusals,
51                            truncated: true,
52                        };
53                    }
54                    point_count = next_point_count;
55                    spans.push(span);
56                }
57                Ok(None) => {}
58                Err(error) if refusals.len() < MAX_REFUSALS => {
59                    refusals.push(LandXmlAlignmentRenderRefusal {
60                        source_id: segment.source_id.0.clone(),
61                        message: error.to_string(),
62                    });
63                }
64                Err(_) => truncated = true,
65            }
66        }
67    }
68    LandXmlAlignmentRenderData {
69        spans,
70        refusals,
71        truncated,
72    }
73}