omf/
array.rs

1use std::marker::PhantomData;
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6#[cfg(feature = "parquet")]
7use crate::data::write_checks::ArrayWriteCheck;
8use crate::{SubblockMode, validate::Reason};
9
10pub trait ArrayType {
11    const DATA_TYPE: DataType;
12}
13
14macro_rules! array_types {
15    ($(#[doc = $doc:literal] $name:ident,)*) => {
16        #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
17        pub enum DataType {
18            $($name,)*
19        }
20
21        pub mod array_type {
22            use super::*;
23            $(
24                #[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
25                pub struct $name {}
26                impl ArrayType for $name {
27                    const DATA_TYPE: DataType = DataType::$name;
28                }
29            )*
30        }
31    };
32}
33
34array_types! {
35    /// An image in PNG or JPEG encoding.
36    Image,
37    /// Floating-point scalar values.
38    Scalar,
39    /// Vertex locations in 3D. Add the project and element origins.
40    Vertex,
41    /// Line segments as indices into a vertex array.
42    Segment,
43    /// Triangles as indices into a vertex array.
44    Triangle,
45    /// Non-nullable category names.
46    Name,
47    /// Non-nullable colormap or category colors.
48    Gradient,
49    /// UV texture coordinates.
50    Texcoord,
51    /// Discrete color-map boundaries.
52    Boundary,
53    /// Parent indices and corners of regular sub-blocks.
54    RegularSubblock,
55    /// Parent indices and corners of free-form sub-blocks.
56    FreeformSubblock,
57    /// Nullable number values, floating-point or signed integer.
58    Number,
59    /// Nullable category index values.
60    Index,
61    /// Nullable 2D or 3D vectors.
62    Vector,
63    /// Nullable text.
64    Text,
65    /// Nullable booleans.
66    Boolean,
67    /// Nullable colors.
68    Color,
69}
70
71#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
72pub struct Array<T> {
73    filename: String,
74    item_count: u64,
75    #[serde(default, skip_serializing, skip_deserializing)]
76    private: ArrayPrivate,
77    #[serde(default, skip_serializing, skip_deserializing)]
78    _marker: PhantomData<T>,
79}
80
81impl<T: ArrayType> Array<T> {
82    pub(crate) fn new(filename: String, item_count: u64) -> Self {
83        Self {
84            filename,
85            item_count,
86            private: Default::default(),
87            _marker: Default::default(),
88        }
89    }
90
91    pub(crate) fn constrain(&mut self, constraint: Constraint) -> Result<(), Reason> {
92        assert_eq!(
93            constraint.data_type(),
94            self.data_type(),
95            "invalid constraint {constraint:?} for {:?} array",
96            self.data_type()
97        );
98        self.private.constraint = Some(constraint);
99        Ok(())
100    }
101
102    pub(crate) fn constraint(&self) -> &Constraint {
103        self.private
104            .constraint
105            .as_ref()
106            .expect("array should have been validated")
107    }
108
109    /// The filename of the array data within the zip file.
110    pub(crate) fn filename(&self) -> &str {
111        &self.filename
112    }
113
114    /// Number of items in the decompressed array. Zero for images.
115    pub fn item_count(&self) -> u64 {
116        self.item_count
117    }
118
119    /// The type of the array, based on `T`.
120    pub fn data_type(&self) -> DataType {
121        T::DATA_TYPE
122    }
123}
124
125#[cfg(feature = "parquet")]
126impl<T: ArrayType> Array<T> {
127    pub(crate) fn add_write_checks(mut self, checks: Vec<ArrayWriteCheck>) -> Self {
128        self.private.checks.extend(checks);
129        self
130    }
131
132    pub(crate) fn run_write_checks(&self) -> Vec<Reason> {
133        let mut reasons = Vec::new();
134        for check in &self.private.checks {
135            if let Err(r) = check.check(self) {
136                reasons.push(r);
137            }
138        }
139        reasons
140    }
141}
142
143#[cfg(not(feature = "parquet"))]
144impl<T: ArrayType> Array<T> {
145    pub(crate) fn run_write_checks(&self) -> Vec<Reason> {
146        Vec::new()
147    }
148}
149
150#[derive(Debug, Default, Clone)]
151struct ArrayPrivate {
152    constraint: Option<Constraint>,
153    #[cfg(feature = "parquet")]
154    checks: Vec<ArrayWriteCheck>,
155}
156
157impl PartialEq for ArrayPrivate {
158    fn eq(&self, _other: &Self) -> bool {
159        // Don't let this private data interfere with tests.
160        true
161    }
162}
163
164#[derive(Debug, Clone, PartialEq)]
165pub(crate) enum Constraint {
166    Image,
167    Scalar,
168    Size,
169    Vertex,
170    Segment(u64),
171    Triangle(u64),
172    Name,
173    Gradient,
174    Texcoord,
175    Boundary,
176    RegularSubblock {
177        block_count: [u32; 3],
178        subblock_count: [u32; 3],
179        mode: Option<SubblockMode>,
180    },
181    FreeformSubblock {
182        block_count: [u32; 3],
183    },
184    Number,
185    Index(u64),
186    Vector,
187    String,
188    Boolean,
189    Color,
190}
191
192impl Constraint {
193    pub fn data_type(&self) -> DataType {
194        match self {
195            Self::Image => DataType::Image,
196            Self::Scalar | Self::Size => DataType::Scalar,
197            Self::Vertex => DataType::Vertex,
198            Self::Segment(_) => DataType::Segment,
199            Self::Triangle(_) => DataType::Triangle,
200            Self::Name => DataType::Name,
201            Self::Gradient => DataType::Gradient,
202            Self::Texcoord => DataType::Texcoord,
203            Self::Boundary => DataType::Boundary,
204            Self::RegularSubblock { .. } => DataType::RegularSubblock,
205            Self::FreeformSubblock { .. } => DataType::FreeformSubblock,
206            Self::Number => DataType::Number,
207            Self::Index(_) => DataType::Index,
208            Self::Vector => DataType::Vector,
209            Self::String => DataType::Text,
210            Self::Boolean => DataType::Boolean,
211            Self::Color => DataType::Color,
212        }
213    }
214}