1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::Value;

use crate::{
    validate::{Validate, Validator},
    Attribute, Color, Geometry, Location,
};

/// Defines a single "object" or "shape" within the OMF file.
///
/// Each shape has a name plus other optional metadata, a "geometry" that describes
/// a point-set, surface, etc., and a list of attributes that that exist on that geometry.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct Element {
    /// The element name. Names should be non-empty and unique.
    pub name: String,
    /// Optional element description.
    #[serde(default, skip_serializing_if = "String::is_empty")]
    pub description: String,
    /// Optional solid color.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub color: Option<Color>,
    /// Arbitrary metadata.
    #[serde(default, skip_serializing_if = "serde_json::Map::is_empty")]
    pub metadata: serde_json::Map<String, Value>,
    /// List of attributes, if any.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub attributes: Vec<Attribute>,
    /// The geometry of the element.
    pub geometry: Geometry,
}

impl Element {
    /// Create a new element with the given name and geometry.
    ///
    /// Geometries will be automatically converted from their objects into enum variants.
    pub fn new(name: impl Into<String>, geometry: impl Into<Geometry>) -> Self {
        Self {
            name: name.into(),
            description: Default::default(),
            metadata: Default::default(),
            attributes: Default::default(),
            geometry: geometry.into(),
            color: None,
        }
    }

    /// Returns the valid locations for attributes on this element.
    pub fn valid_locations(&self) -> &'static [Location] {
        self.geometry.valid_locations()
    }

    /// Returns the number of values needed for the given location.
    pub fn location_len(&self, location: Location) -> Option<u64> {
        self.geometry.location_len(location)
    }
}

impl Validate for Element {
    fn validate_inner(&mut self, val: &mut Validator) {
        val.enter("Element")
            .name(&self.name)
            .obj(&mut self.geometry)
            .objs(&mut self.attributes)
            .unique(
                self.attributes.iter().map(|a| &a.name),
                "attributes[..]::name",
                false,
            )
            .attrs_on_geometry(&self.attributes, &self.geometry);
    }
}