1mod de;
42mod error;
43mod ser;
44
45pub use de::{
46 chunked::{PlyChunkedReader, RowVisitor},
47 PlyReader,
48};
49pub use de::{from_bytes, from_reader, from_str};
50pub use error::{DeserializeError, SerializeError};
51pub use ser::{to_bytes, to_string, to_writer, SerializeOptions};
52
53use std::io::BufRead;
54
55use std::fmt::{self, Display};
56use std::str::FromStr;
57
58#[derive(Debug, Clone, Copy, PartialEq)]
62pub enum PlyFormat {
63 Ascii,
65 BinaryLittleEndian,
67 BinaryBigEndian,
69}
70
71impl fmt::Display for PlyFormat {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 match self {
74 PlyFormat::Ascii => write!(f, "ascii"),
75 PlyFormat::BinaryLittleEndian => write!(f, "binary_little_endian"),
76 PlyFormat::BinaryBigEndian => write!(f, "binary_big_endian"),
77 }
78 }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq)]
86pub enum ScalarType {
87 I8,
88 U8,
89 I16,
90 U16,
91 I32,
92 U32,
93 F32,
94 F64,
95}
96
97impl ScalarType {
98 pub(crate) fn parse(s: &str) -> Result<Self, DeserializeError> {
99 match s {
100 "char" | "int8" => Ok(ScalarType::I8),
101 "uchar" | "uint8" => Ok(ScalarType::U8),
102 "short" | "int16" => Ok(ScalarType::I16),
103 "ushort" | "uint16" => Ok(ScalarType::U16),
104 "int" | "int32" => Ok(ScalarType::I32),
105 "uint" | "uint32" => Ok(ScalarType::U32),
106 "float" | "float32" => Ok(ScalarType::F32),
107 "double" | "float64" => Ok(ScalarType::F64),
108 _ => Err(DeserializeError(std::io::Error::new(
109 std::io::ErrorKind::InvalidData,
110 format!("Unknown scalar type: {}", s),
111 ))),
112 }
113 }
114}
115
116impl FromStr for ScalarType {
117 type Err = DeserializeError;
118
119 fn from_str(s: &str) -> Result<Self, Self::Err> {
120 Self::parse(s)
121 }
122}
123
124impl Display for ScalarType {
125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126 match self {
127 ScalarType::I8 => write!(f, "int8"),
128 ScalarType::U8 => write!(f, "uint8"),
129 ScalarType::I16 => write!(f, "int16"),
130 ScalarType::U16 => write!(f, "uint16"),
131 ScalarType::I32 => write!(f, "int32"),
132 ScalarType::U32 => write!(f, "uint32"),
133 ScalarType::F32 => write!(f, "float32"),
134 ScalarType::F64 => write!(f, "float64"),
135 }
136 }
137}
138
139#[derive(Debug, Clone)]
144pub enum PropertyType {
145 Scalar(ScalarType),
147 List {
149 count_type: ScalarType,
150 data_type: ScalarType,
151 },
152}
153
154#[derive(Debug, Clone)]
158pub struct PlyProperty {
159 pub name: String,
160 pub property_type: PropertyType,
161}
162
163#[derive(Debug, Clone)]
168pub struct ElementDef {
169 pub name: String,
170 pub count: usize,
171 pub properties: Vec<PlyProperty>,
172}
173
174impl ElementDef {
175 pub fn get_property(&self, name: &str) -> Option<&PlyProperty> {
177 self.properties.iter().find(|p| p.name == name)
178 }
179
180 pub fn has_property(&self, name: &str) -> bool {
182 self.get_property(name).is_some()
183 }
184}
185
186#[derive(Debug, Clone)]
191pub struct PlyHeader {
192 pub format: PlyFormat,
193 pub elem_defs: Vec<ElementDef>,
194 pub comments: Vec<String>,
195 pub obj_info: Vec<String>,
196}
197
198impl PlyHeader {
199 pub(crate) fn parse<R: BufRead>(mut reader: R) -> Result<Self, DeserializeError> {
200 let mut line = String::new();
201 reader.read_line(&mut line)?;
202 if line.trim() != "ply" {
203 return Err(DeserializeError(std::io::Error::new(
204 std::io::ErrorKind::InvalidData,
205 "File must start with 'ply'",
206 )));
207 }
208
209 let mut format = None;
210 let mut elements = Vec::new();
211 let mut comments = Vec::new();
212 let mut obj_info = Vec::new();
213 let mut current_element: Option<ElementDef> = None;
214
215 loop {
216 let mut line = String::new();
217 let bytes_read = reader.read_line(&mut line)?;
218 if bytes_read == 0 {
219 return Err(DeserializeError(std::io::Error::new(
220 std::io::ErrorKind::UnexpectedEof,
221 "Unexpected end of file",
222 )));
223 }
224
225 if line.trim() == "end_header" && line.ends_with("\n") {
229 break;
230 }
231 let parts: Vec<&str> = line.split_whitespace().collect();
232 if parts.is_empty() {
233 continue;
234 }
235 match parts[0] {
236 "format" => {
237 if parts.len() < 3 {
238 return Err(DeserializeError(std::io::Error::new(
239 std::io::ErrorKind::InvalidData,
240 "Invalid format line",
241 )));
242 }
243 format = Some(match parts[1] {
244 "ascii" => PlyFormat::Ascii,
245 "binary_little_endian" => PlyFormat::BinaryLittleEndian,
246 "binary_big_endian" => PlyFormat::BinaryBigEndian,
247 _ => {
248 return Err(DeserializeError(std::io::Error::new(
249 std::io::ErrorKind::InvalidData,
250 format!("Unknown format: {}", parts[1]),
251 )))
252 }
253 });
254 }
255 "comment" => {
256 comments.push(parts[1..].join(" "));
257 }
258 "obj_info" => {
259 obj_info.push(parts[1..].join(" "));
260 }
261 "element" => {
262 if parts.len() < 3 {
263 return Err(DeserializeError(std::io::Error::new(
264 std::io::ErrorKind::InvalidData,
265 "Invalid element line",
266 )));
267 }
268
269 if let Some(element) = current_element.take() {
270 elements.push(element);
271 }
272
273 let name = parts[1].to_string();
274 let count = parts[2].parse::<usize>().map_err(|_| {
275 DeserializeError(std::io::Error::new(
276 std::io::ErrorKind::InvalidData,
277 format!("Invalid element count: {}", parts[2]),
278 ))
279 })?;
280
281 current_element = Some(ElementDef {
282 name,
283 count,
284 properties: Vec::new(),
285 });
286 }
287 "property" => {
288 let element = current_element.as_mut().ok_or_else(|| {
289 DeserializeError(std::io::Error::new(
290 std::io::ErrorKind::InvalidData,
291 "Property without element",
292 ))
293 })?;
294
295 if parts.len() < 3 {
296 return Err(DeserializeError(std::io::Error::new(
297 std::io::ErrorKind::InvalidData,
298 "Invalid property line",
299 )));
300 }
301
302 if parts[1] == "list" {
303 if parts.len() < 5 {
305 return Err(DeserializeError(std::io::Error::new(
306 std::io::ErrorKind::InvalidData,
307 "Invalid list property line",
308 )));
309 }
310 let count_type = ScalarType::parse(parts[2])?;
311 let data_type = ScalarType::parse(parts[3])?;
312 let name = parts[4].to_string();
313
314 element.properties.push(PlyProperty {
315 property_type: PropertyType::List {
316 count_type,
317 data_type,
318 },
319 name,
320 });
321 } else {
322 let data_type = ScalarType::parse(parts[1])?;
323 let name = parts[2].to_string();
324
325 element.properties.push(PlyProperty {
326 property_type: PropertyType::Scalar(data_type),
327 name,
328 });
329 }
330 }
331 _ => {}
332 }
333 }
334 if let Some(element) = current_element {
335 elements.push(element);
336 }
337 let format = format.ok_or_else(|| {
338 DeserializeError(std::io::Error::new(
339 std::io::ErrorKind::InvalidData,
340 "Missing format specification",
341 ))
342 })?;
343 Ok(PlyHeader {
344 format,
345 elem_defs: elements,
346 comments,
347 obj_info,
348 })
349 }
350
351 pub fn get_element(&self, name: &str) -> Option<ElementDef> {
353 self.elem_defs.iter().find(|e| e.name == name).cloned()
354 }
355
356 pub fn has_element(&self, name: &str) -> bool {
358 self.elem_defs.iter().any(|e| e.name == name)
359 }
360}
361
362#[derive(Debug)]
379pub struct ListCountU16<T>(pub T);
380
381#[derive(Debug)]
397pub struct ListCountU32<T>(pub T);
398
399macro_rules! impl_list_count_traits {
401 ($wrapper:ident) => {
402 impl<T> From<T> for $wrapper<T> {
403 fn from(inner: T) -> Self {
404 $wrapper(inner)
405 }
406 }
407 impl<T> std::ops::Deref for $wrapper<T> {
408 type Target = T;
409 fn deref(&self) -> &Self::Target {
410 &self.0
411 }
412 }
413
414 impl<T> std::ops::DerefMut for $wrapper<T> {
415 fn deref_mut(&mut self) -> &mut Self::Target {
416 &mut self.0
417 }
418 }
419
420 impl<T> serde::Serialize for $wrapper<T>
421 where
422 T: serde::Serialize,
423 {
424 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
425 where
426 S: serde::Serializer,
427 {
428 serializer.serialize_newtype_struct(stringify!($wrapper), &self.0)
429 }
430 }
431
432 impl<'de, T> serde::Deserialize<'de> for $wrapper<T>
433 where
434 T: serde::Deserialize<'de>,
435 {
436 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
437 where
438 D: serde::Deserializer<'de>,
439 {
440 T::deserialize(deserializer).map($wrapper)
441 }
442 }
443 };
444}
445
446impl_list_count_traits!(ListCountU16);
447impl_list_count_traits!(ListCountU32);