Skip to main content

vox_format/
types.rs

1//! Basic types
2
3use std::{
4    collections::HashMap,
5    convert::{
6        TryFrom,
7        TryInto,
8    },
9    fmt,
10    io::{
11        Read,
12        Write,
13    },
14    ops::Index,
15};
16
17use byteorder::{
18    ReadBytesExt,
19    WriteBytesExt,
20    LE,
21};
22#[cfg(feature = "serialize")]
23use serde::{
24    Deserialize,
25    Serialize,
26};
27use thiserror::Error;
28
29use crate::{
30    default_palette::DEFAULT_PALETTE,
31    reader::Error as ReadError,
32    writer::Error as WriteError,
33};
34
35/// The version of a `.VOX` file. This is a wrapper around a `u32` and
36/// implements `Default` and the [`Version::is_supported`] method.
37#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
38#[cfg_attr(
39    feature = "serialize",
40    derive(Serialize, Deserialize),
41    serde(transparent)
42)]
43pub struct Version(pub u32);
44
45impl Version {
46    /// Returns whether this version is supported.
47    pub fn is_supported(&self) -> bool {
48        *self == Self::default()
49    }
50}
51
52impl Default for Version {
53    fn default() -> Self {
54        Self(150)
55    }
56}
57
58impl fmt::Display for Version {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        self.0.fmt(f)
61    }
62}
63
64impl Version {
65    /// Reads a version from a [`std::io::Read`].
66    pub fn read<R: Read>(mut reader: R) -> Result<Self, ReadError> {
67        Ok(Self(reader.read_u32::<LE>()?))
68    }
69
70    /// Writes the version to a [`std::io::Write`].
71    pub fn write<W: Write>(&self, mut writer: W) -> Result<(), WriteError> {
72        writer.write_u32::<LE>(self.0)?;
73        Ok(())
74    }
75}
76
77#[derive(Clone, Debug)]
78#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
79/// A collection of voxels with a specific size.
80///
81/// Note, that the voxels are
82/// stored as a `Vec`, like they're stored in-file. Therefore there is no
83/// efficient point-query for voxels. If you need your models to support
84/// point-queries, you must implement your own [`crate::data::VoxBuffer`] or
85/// [`crate::data::VoxModelBuffer`]. For testing and convienience there is
86/// [`Model::get_voxel`] to perform a point-query with a linear search.
87pub struct Model {
88    /// Size of the model in voxels.
89    pub size: Size,
90    pub voxels: Vec<Voxel>,
91}
92
93impl Model {
94    /// Looks up the voxel with the coordinates of `point`. Since [`Model`]
95    /// stores [`Voxel`]s in a `Vec`, this performs a linear search, and should
96    /// be avoided if possible.
97    pub fn get_voxel(&self, point: Vector<i8>) -> Option<&Voxel> {
98        self.voxels.iter().find(|voxel| voxel.point == point)
99    }
100}
101
102#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
103#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
104pub struct Voxel {
105    pub point: Point,
106    pub color_index: ColorIndex,
107}
108
109impl Voxel {
110    /// Creates a new voxel
111    ///
112    /// # Arguments
113    ///
114    /// - `point`: The point location of the voxel.
115    /// - `color_index`: The color index for this voxel.
116    ///
117    /// # Returns
118    ///
119    /// The newly created voxel.
120    pub fn new(point: impl Into<Point>, color_index: impl Into<ColorIndex>) -> Self {
121        Self {
122            point: point.into(),
123            color_index: color_index.into(),
124        }
125    }
126
127    /// Reads a voxel from a [`std::io::Read`].
128    pub fn read<R: Read>(mut reader: R) -> Result<Self, ReadError> {
129        Ok(Self {
130            point: Point::read(&mut reader)?,
131            color_index: ColorIndex::read(&mut reader)?,
132        })
133    }
134
135    /// Writes the voxel to a [`std::io::Write`].
136    pub fn write<W: Write>(&self, mut writer: W) -> Result<(), WriteError> {
137        self.point.write(&mut writer)?;
138        self.color_index.write(&mut writer)?;
139        Ok(())
140    }
141}
142
143#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
144#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
145pub struct Vector<T> {
146    pub x: T,
147    pub y: T,
148    pub z: T,
149}
150
151impl<T> Vector<T> {
152    /// Creates a vector from its components.
153    pub fn new(x: T, y: T, z: T) -> Self {
154        Self { x, y, z }
155    }
156}
157
158impl Vector<i8> {
159    /// Reads a vector from a [`std::io::Read`].
160    pub fn read<R: Read>(mut reader: R) -> Result<Self, ReadError> {
161        Ok(Self {
162            x: reader.read_i8()?,
163            y: reader.read_i8()?,
164            z: reader.read_i8()?,
165        })
166    }
167
168    /// Writes the vector to a [`std::io::Write`].
169    pub fn write<W: Write>(&self, mut writer: W) -> Result<(), WriteError> {
170        writer.write_i8(self.x)?;
171        writer.write_i8(self.y)?;
172        writer.write_i8(self.z)?;
173        Ok(())
174    }
175}
176
177impl Vector<u32> {
178    /// Reads a vector from a [`std::io::Read`].
179    pub fn read<R: Read>(mut reader: R) -> Result<Self, ReadError> {
180        Ok(Self {
181            x: reader.read_u32::<LE>()?,
182            y: reader.read_u32::<LE>()?,
183            z: reader.read_u32::<LE>()?,
184        })
185    }
186
187    /// Writes the vector to a [`std::io::Write`].
188    pub fn write<W: Write>(&self, mut writer: W) -> Result<(), WriteError> {
189        writer.write_u32::<LE>(self.x)?;
190        writer.write_u32::<LE>(self.y)?;
191        writer.write_u32::<LE>(self.z)?;
192        Ok(())
193    }
194}
195
196impl<T> From<[T; 3]> for Vector<T> {
197    fn from(v: [T; 3]) -> Self {
198        let [x, y, z] = v;
199        Self::new(x, y, z)
200    }
201}
202
203impl<T> From<Vector<T>> for [T; 3] {
204    fn from(v: Vector<T>) -> Self {
205        [v.x, v.y, v.z]
206    }
207}
208
209impl<T: fmt::Debug> fmt::Debug for Vector<T> {
210    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211        write!(f, "({:?}, {:?}, {:?})", self.x, self.y, self.z)
212    }
213}
214
215pub type Point = Vector<i8>;
216pub type Size = Vector<u32>;
217
218/// A color palette. This contains colors indexec by `u8`. It is used to look up
219/// colors of a voxel.
220///
221/// If you need MagicaVoxel's default palette, you can either use
222/// [`crate::default_palette::DEFAULT_PALETTE`], or [`Palette::default`].
223#[derive(Clone, Debug)]
224#[cfg_attr(
225    feature = "serialize",
226    derive(Serialize, Deserialize),
227    serde(transparent)
228)]
229pub struct Palette {
230    /// The colors of the palette.
231    ///
232    /// MagicaVoxel always set color 0 to be fully transparent. We don't enfore
233    /// this, but this palette entry will never be written to a `.VOX` file.
234    #[cfg_attr(feature = "serialize", serde(with = "serde_big_array::BigArray"))]
235    pub colors: [Color; 256],
236}
237
238impl Default for Palette {
239    fn default() -> Self {
240        DEFAULT_PALETTE.clone()
241    }
242}
243
244impl Palette {
245    /// Tests whether this is the [`crate::default_palette::DEFAULT_PALETTE`].
246    pub fn is_default(&self) -> bool {
247        self.colors == DEFAULT_PALETTE.colors
248    }
249
250    /// Returns the color for an index. Since all color indices are valid, this
251    /// always returns a value. This is equivalent to `palette[index]`.
252    pub fn get(&self, color_index: ColorIndex) -> Color {
253        self.colors[color_index.0 as usize]
254    }
255
256    /// Creates an iterator over all colors.
257    ///
258    /// ```
259    /// # let palette = vox_format::types::Palette::default();
260    /// for (index, color) in palette.iter() {
261    ///     println!("{} -> {:?}", index, color);
262    /// }
263    /// ```
264    pub fn iter(&self) -> PaletteIter {
265        PaletteIter {
266            inner: self.colors.iter().enumerate(),
267        }
268    }
269
270    /// Reads a color palette from a [`std::io::Read`].
271    pub fn read<R: Read>(mut reader: R) -> Result<Self, ReadError> {
272        let mut palette = Palette::default();
273
274        for i in 0..255 {
275            palette.colors[i + 1] = Color::read(&mut reader)?;
276        }
277
278        Ok(palette)
279    }
280
281    /// Writes the color palette to a [`std::io::Write`].
282    ///
283    /// MagicaVoxel fixes color 0 to be always fully transparent, and doesn't
284    /// include it in the file. Therefore this method will ignore the value
285    /// for color 0.
286    pub fn write<W: Write>(&self, mut writer: W) -> Result<(), WriteError> {
287        for color in &self.colors[1..] {
288            color.write(&mut writer)?;
289        }
290
291        Ok(())
292    }
293}
294
295/// An iterator over entries in a [`Palette`]. This is created with
296/// [`Palette::iter`].
297#[derive(Debug)]
298pub struct PaletteIter<'a> {
299    inner: std::iter::Enumerate<std::slice::Iter<'a, Color>>,
300}
301
302impl<'a> Iterator for PaletteIter<'a> {
303    type Item = (ColorIndex, Color);
304
305    fn next(&mut self) -> Option<Self::Item> {
306        let (index, color) = self.inner.next()?;
307        Some((ColorIndex(index as u8), *color))
308    }
309}
310
311impl Index<ColorIndex> for Palette {
312    type Output = Color;
313
314    fn index(&self, color_index: ColorIndex) -> &Self::Output {
315        &self.colors[color_index.0 as usize]
316    }
317}
318
319/// A palette of materials
320///
321/// # Work-in-Progress
322///
323/// This interface his likely to change in the future and is not fully
324/// implemented yet.
325#[derive(Clone, Debug, Default)]
326#[cfg_attr(
327    feature = "serialize",
328    derive(Serialize, Deserialize),
329    serde(transparent)
330)]
331pub struct MaterialPalette {
332    /// TODO: Does the material ID correspond to a ColorIndex?
333    materials: HashMap<ColorIndex, Material>,
334}
335
336impl MaterialPalette {
337    /// Tests if the material palette is empty.
338    pub fn is_empty(&self) -> bool {
339        self.materials.is_empty()
340    }
341
342    /// Returns the  material with ID `material_id` from the palette. Returns
343    /// `None`, if there is no material with this ID. This is equivalent to
344    /// `material_palette[material_id]`.
345    pub fn get(&self, material_id: ColorIndex) -> Option<&Material> {
346        self.materials.get(&material_id)
347    }
348
349    /// Creates an iterator over all materials.
350    ///
351    /// ```
352    /// # let material_palette = vox_format::types::MaterialPalette::default();
353    /// for (id, material) in material_palette.iter() {
354    ///     println!("{} -> {:#?}", id, material);
355    /// }
356    /// ```
357    ///
358    /// # Work-in-Progress
359    ///
360    /// This interface his likely to change in the future and is not fully
361    /// implemented yet.
362    pub fn iter(&self) -> MaterialPaletteIter {
363        MaterialPaletteIter {
364            inner: self.materials.iter(),
365        }
366    }
367}
368
369/// An iterator over entries in a [`MaterialPalette`]. This is created with
370/// [`MaterialPalette::iter`].
371#[derive(Debug)]
372pub struct MaterialPaletteIter<'a> {
373    inner: std::collections::hash_map::Iter<'a, ColorIndex, Material>,
374}
375
376impl<'a> Iterator for MaterialPaletteIter<'a> {
377    type Item = (ColorIndex, &'a Material);
378
379    fn next(&mut self) -> Option<Self::Item> {
380        let (index, material) = self.inner.next()?;
381        Some((*index, material))
382    }
383}
384
385/// An 8-bit RGBA color.
386#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
387#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
388pub struct Color {
389    /// Red channel
390    pub r: u8,
391
392    /// Green channel
393    pub g: u8,
394
395    /// Blue channel
396    pub b: u8,
397
398    /// Alpha channel. `0` is fully transparent, `255` is fully opaque.
399    pub a: u8,
400}
401
402impl Color {
403    /// Creates a new color from its channels.
404    pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
405        Self { r, g, b, a }
406    }
407
408    /// Reads a color from a [`std::io::Read`].
409    pub fn read<R: Read>(mut reader: R) -> Result<Self, ReadError> {
410        // FIXME: I think color is stored in ABGR format.
411        Ok(Self {
412            r: reader.read_u8()?,
413            g: reader.read_u8()?,
414            b: reader.read_u8()?,
415            a: reader.read_u8()?,
416        })
417    }
418
419    /// Writes the color to a [`std::io::Write`].
420    pub fn write<W: Write>(&self, mut writer: W) -> Result<(), WriteError> {
421        // FIXME: I think color is stored in ABGR format.
422        writer.write_u8(self.r)?;
423        writer.write_u8(self.g)?;
424        writer.write_u8(self.b)?;
425        writer.write_u8(self.a)?;
426        Ok(())
427    }
428
429    /// A light-blue color. This is the color that is selected by MagicaVoxel by
430    /// default. It has index `79` in the [default
431    /// palette](`crate::default_palette::DEFAULT_PALETTE`)
432    pub fn light_blue() -> Self {
433        Self {
434            r: 153,
435            g: 204,
436            b: 255,
437            a: 255,
438        }
439    }
440}
441
442impl From<Color> for [u8; 4] {
443    fn from(color: Color) -> Self {
444        [color.r, color.g, color.b, color.a]
445    }
446}
447
448impl From<[u8; 4]> for Color {
449    fn from(color: [u8; 4]) -> Self {
450        Self {
451            r: color[0],
452            g: color[1],
453            b: color[2],
454            a: color[3],
455        }
456    }
457}
458
459#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
460#[cfg_attr(
461    feature = "serialize",
462    derive(Serialize, Deserialize),
463    serde(transparent)
464)]
465pub struct ColorIndex(pub u8);
466
467impl ColorIndex {
468    /// Reads a color index from a [`std::io::Read`].
469    pub fn read<R: Read>(mut reader: R) -> Result<Self, ReadError> {
470        Ok(Self(reader.read_u8()?))
471    }
472
473    /// Writes the color index to a [`std::io::Write`].
474    pub fn write<W: Write>(&self, mut writer: W) -> Result<(), WriteError> {
475        writer.write_u8(self.0)?;
476        Ok(())
477    }
478
479    /// The index selected by default (79). With the default palette this is a
480    /// [light-blue](`Color::light_blue`).
481    pub fn default_index() -> Self {
482        Self(79)
483    }
484}
485
486impl From<u8> for ColorIndex {
487    fn from(x: u8) -> Self {
488        Self(x)
489    }
490}
491
492impl From<ColorIndex> for u8 {
493    fn from(x: ColorIndex) -> Self {
494        x.0
495    }
496}
497
498impl fmt::Display for ColorIndex {
499    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
500        self.0.fmt(f)
501    }
502}
503
504/// A material definition.
505///
506/// # Work-in-Progress
507///
508/// This interface his likely to change in the future and is not fully
509/// implemented yet.
510#[derive(Clone, Debug)]
511#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
512pub struct Material {
513    /// The type of material.
514    pub ty: MaterialType,
515
516    /// The mateiral weight. This has a different meaning depending on the
517    /// material type:
518    ///  - [`MaterialType::Diffuse`]: Always `1.0`.
519    ///  - [`MaterialType::Metal`]: Blends between metal and diffuse material.
520    ///    Must be in interval `(0.0, 1.0]`.
521    ///  - [`MaterialType::Glass`]: Blends between glass and diffuse material.
522    ///    Must be in interval `(0.0, 1.0]`.
523    ///  - [`MaterialType::Emissive`]: The intensity of emitted light. Must be
524    ///    in interval `(0.0, 1.0]`.
525    pub weight: f32,
526
527    pub plastic: Option<f32>,
528    pub roughness: Option<f32>,
529    pub specular: Option<f32>,
530    pub ior: Option<f32>,
531    pub attenuation: Option<f32>,
532    pub power: Option<f32>,
533    pub glow: Option<f32>,
534    pub is_total_power: bool,
535}
536
537impl Material {
538    /// Reads a material definition from a [`std::io::Read`].
539    pub fn read<R: Read>(mut reader: R) -> Result<Self, ReadError> {
540        let ty = MaterialType::read(&mut reader)?;
541        let weight = reader.read_f32::<LE>()?;
542        let flags = reader.read_u32::<LE>()?;
543
544        let plastic = (flags & 1 != 0)
545            .then(|| reader.read_f32::<LE>())
546            .transpose()?;
547        let roughness = (flags & 2 != 0)
548            .then(|| reader.read_f32::<LE>())
549            .transpose()?;
550        let specular = (flags & 4 != 0)
551            .then(|| reader.read_f32::<LE>())
552            .transpose()?;
553        let ior = (flags & 8 != 0)
554            .then(|| reader.read_f32::<LE>())
555            .transpose()?;
556        let attenuation = (flags & 16 != 0)
557            .then(|| reader.read_f32::<LE>())
558            .transpose()?;
559        let power = (flags & 32 != 0)
560            .then(|| reader.read_f32::<LE>())
561            .transpose()?;
562        let glow = (flags & 64 != 0)
563            .then(|| reader.read_f32::<LE>())
564            .transpose()?;
565
566        Ok(Material {
567            ty,
568            weight,
569            plastic,
570            roughness,
571            specular,
572            ior,
573            attenuation,
574            power,
575            glow,
576            is_total_power: (flags & 128 != 0),
577        })
578    }
579}
580
581/// A material type.
582///
583/// # Work-in-Progress
584///
585/// This interface his likely to change in the future and is not fully
586/// implemented yet.
587#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
588#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
589pub enum MaterialType {
590    Diffuse,
591    Metal,
592    Glass,
593    Emissive,
594}
595
596impl TryFrom<u8> for MaterialType {
597    type Error = MaterialTryFromError;
598
599    fn try_from(x: u8) -> Result<Self, Self::Error> {
600        match x {
601            0 => Ok(MaterialType::Diffuse),
602            1 => Ok(MaterialType::Metal),
603            2 => Ok(MaterialType::Glass),
604            3 => Ok(MaterialType::Emissive),
605            x => Err(MaterialTryFromError(x)),
606        }
607    }
608}
609
610impl From<MaterialType> for u8 {
611    fn from(ty: MaterialType) -> Self {
612        match ty {
613            MaterialType::Diffuse => 0,
614            MaterialType::Metal => 1,
615            MaterialType::Glass => 2,
616            MaterialType::Emissive => 3,
617        }
618    }
619}
620
621impl MaterialType {
622    /// Reads a material type from a [`std::io::Read`].
623    pub fn read<R: Read>(mut reader: R) -> Result<Self, ReadError> {
624        reader
625            .read_u8()?
626            .try_into()
627            .map_err(|e: MaterialTryFromError| ReadError::InvalidMaterial { material_type: e.0 })
628    }
629
630    pub fn write<W: Write>(&self, mut writer: W) -> Result<(), WriteError> {
631        writer.write_u8((*self).into())?;
632        Ok(())
633    }
634}
635
636/// A transform node.
637///
638/// # Work-in-Progress
639///
640/// This interface his likely to change in the future and is not fully
641/// implemented yet.
642#[derive(Debug, Error)]
643#[error("Invalid material type: {0}")]
644pub struct MaterialTryFromError(pub u8);
645
646#[derive(Clone, Debug)]
647#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
648pub struct Transform {
649    pub node_id: u32,
650    pub attributes: Attributes,
651    pub child_node_id: u32,
652    pub reserved_id: Option<u32>,
653    pub layer_id: Option<u32>,
654    pub frames: Vec<Attributes>,
655}
656
657impl Transform {
658    /// Reads a transform node from a [`std::io::Read`].
659    pub fn read<R: Read>(mut reader: R) -> Result<Self, ReadError> {
660        let node_id = reader.read_u32::<LE>()?;
661        let attributes = Attributes::read(&mut reader)?;
662        let child_node_id = reader.read_u32::<LE>()?;
663        let reserved_id = read_id_opt(&mut reader)?;
664        let layer_id = read_id_opt(&mut reader)?;
665
666        let num_frames = reader.read_u32::<LE>()?;
667        let mut frames = vec![];
668        for _ in 0..num_frames {
669            frames.push(Attributes::read(&mut reader)?);
670        }
671
672        Ok(Self {
673            node_id,
674            attributes,
675            child_node_id,
676            reserved_id,
677            layer_id,
678            frames,
679        })
680    }
681
682    pub fn get_transform(&self, frame: usize) -> Option<Vector<i32>> {
683        let mut parts = self.frames.get(frame)?.get("_t")?.split_whitespace();
684        let x = parts.next()?.parse().ok()?;
685        let y = parts.next()?.parse().ok()?;
686        let z = parts.next()?.parse().ok()?;
687
688        parts.next().is_none().then(|| Vector::new(x, y, z))
689    }
690}
691
692/// A group node.
693///
694/// # Work-in-Progress
695///
696/// This interface his likely to change in the future and is not fully
697/// implemented yet.
698#[derive(Clone, Debug)]
699#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
700pub struct Group {
701    pub node_id: u32,
702    pub attributes: Attributes,
703    pub children: Vec<u32>,
704}
705
706impl Group {
707    /// Reads a group from a [`std::io::Read`].
708    pub fn read<R: Read>(mut reader: R) -> Result<Self, ReadError> {
709        let node_id = reader.read_u32::<LE>()?;
710        let attributes = Attributes::read(&mut reader)?;
711        let num_children = reader.read_u32::<LE>()?;
712        let mut children = Vec::with_capacity(num_children as usize);
713
714        for _ in 0..num_children {
715            children.push(reader.read_u32::<LE>()?);
716        }
717
718        Ok(Self {
719            node_id,
720            attributes,
721            children,
722        })
723    }
724}
725
726/// A shape node.
727///
728/// # Work-in-Progress
729///
730/// This interface his likely to change in the future and is not fully
731/// implemented yet.
732#[derive(Clone, Debug)]
733#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
734pub struct Shape {
735    pub node_id: u32,
736    pub attributes: Attributes,
737}
738
739impl Shape {
740    /// Reads a shape node from a [`std::io::Read`].
741    pub fn read<R: Read>(mut reader: R) -> Result<Self, ReadError> {
742        Ok(Self {
743            node_id: reader.read_u32::<LE>()?,
744            attributes: Attributes::read(reader)?,
745        })
746    }
747}
748
749/// A layer node.
750///
751/// # Work-in-Progress
752///
753/// This interface his likely to change in the future and is not fully
754/// implemented yet.
755#[derive(Clone, Debug)]
756#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
757pub struct Layer {
758    pub node_id: u32,
759    pub attributes: Attributes,
760    pub reserved_id: Option<u32>,
761}
762
763impl Layer {
764    /// Reads a layer node from a [`std::io::Read`].
765    pub fn read<R: Read>(mut reader: R) -> Result<Self, ReadError> {
766        Ok(Self {
767            node_id: reader.read_u32::<LE>()?,
768            attributes: Attributes::read(&mut reader)?,
769            reserved_id: read_id_opt(reader)?,
770        })
771    }
772}
773
774/// Node attributes. These contain meta-data for nodes, such as [`Transform`] or
775/// [`Layer`].
776#[derive(Clone, Debug, Default)]
777#[cfg_attr(
778    feature = "serialize",
779    derive(Serialize, Deserialize),
780    serde(transparent)
781)]
782pub struct Attributes {
783    inner: HashMap<String, String>,
784}
785
786impl Attributes {
787    /// Reads attributes from a [`std::io::Read`].
788    pub fn read<R: Read>(mut reader: R) -> Result<Self, ReadError> {
789        // An array of key value pairs, where key and value are strings prefixed with
790        // length as u32
791
792        let mut inner = HashMap::new();
793        let num_items = reader.read_u32::<LE>()?;
794        log::trace!("Attributes::read: num_items={}", num_items);
795        for _ in 0..num_items {
796            let key = Self::read_string(&mut reader)?;
797            let value = Self::read_string(&mut reader)?;
798            log::trace!("Attributes::read: key={}, value={}", key, value);
799            inner.insert(key, value);
800        }
801
802        Ok(Attributes { inner })
803    }
804
805    fn read_string<R: Read>(mut reader: R) -> Result<String, ReadError> {
806        let len = reader.read_u32::<LE>()?;
807        log::trace!("Attributes::read_string: len={}", len);
808        let mut buf = vec![0; len.try_into().expect("int overflow")];
809        reader.read_exact(&mut buf)?;
810        log::trace!("Attributes::read_string: buf={:?}", buf);
811        Ok(String::from_utf8(buf)?)
812    }
813
814    /// Returns the attribute with the given key, or `None`, if no such
815    /// attribute exists.
816    pub fn get(&self, key: impl AsRef<str>) -> Option<&str> {
817        Some(self.inner.get(key.as_ref())?.as_str())
818    }
819
820    /// Creates an iterator over the attributes. The iterator returns items
821    /// `(&str, &str)`.
822    pub fn iter(&self) -> AttributesIter {
823        AttributesIter {
824            inner: self.inner.iter(),
825        }
826    }
827}
828
829/// An interator over attributes. Created with [`Attributes::iter`].
830#[derive(Debug)]
831pub struct AttributesIter<'a> {
832    inner: std::collections::hash_map::Iter<'a, String, String>,
833}
834
835impl<'a> Iterator for AttributesIter<'a> {
836    type Item = (&'a str, &'a str);
837
838    fn next(&mut self) -> Option<Self::Item> {
839        let (key, value) = self.inner.next()?;
840        Some((key.as_ref(), value.as_ref()))
841    }
842}
843
844fn read_id_opt<R: Read>(mut reader: R) -> Result<Option<u32>, ReadError> {
845    Ok(reader.read_i32::<LE>()?.try_into().ok())
846}