1use ifc_model::EntityId;
4
5use crate::error::{StyleError, StyleResult};
6use crate::view::Record;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum TextPath {
11 Left,
13 Right,
15 Up,
17 Down,
19}
20
21impl TextPath {
22 pub(crate) const fn as_ifc(self) -> &'static str {
23 match self {
24 Self::Left => "LEFT",
25 Self::Right => "RIGHT",
26 Self::Up => "UP",
27 Self::Down => "DOWN",
28 }
29 }
30
31 fn parse(value: &str, id: EntityId) -> StyleResult<Self> {
32 match value.to_ascii_uppercase().as_str() {
33 "LEFT" => Ok(Self::Left),
34 "RIGHT" => Ok(Self::Right),
35 "UP" => Ok(Self::Up),
36 "DOWN" => Ok(Self::Down),
37 _ => Err(StyleError::InvalidValue {
38 entity: "IFCTEXTLITERAL".to_owned(),
39 id,
40 attribute: "Path",
41 value: value.to_owned(),
42 }),
43 }
44 }
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub enum BoxAlignment {
50 TopLeft,
52 TopMiddle,
54 TopRight,
56 MiddleLeft,
58 Center,
60 MiddleRight,
62 BottomLeft,
64 BottomMiddle,
66 BottomRight,
68}
69
70impl BoxAlignment {
71 pub(crate) const fn as_ifc(self) -> &'static str {
72 match self {
73 Self::TopLeft => "top-left",
74 Self::TopMiddle => "top-middle",
75 Self::TopRight => "top-right",
76 Self::MiddleLeft => "middle-left",
77 Self::Center => "center",
78 Self::MiddleRight => "middle-right",
79 Self::BottomLeft => "bottom-left",
80 Self::BottomMiddle => "bottom-middle",
81 Self::BottomRight => "bottom-right",
82 }
83 }
84
85 fn parse(value: &str, record: Record<'_, '_>) -> StyleResult<Self> {
86 match value.to_ascii_lowercase().as_str() {
87 "top-left" => Ok(Self::TopLeft),
88 "top-middle" => Ok(Self::TopMiddle),
89 "top-right" => Ok(Self::TopRight),
90 "middle-left" => Ok(Self::MiddleLeft),
91 "center" => Ok(Self::Center),
92 "middle-right" => Ok(Self::MiddleRight),
93 "bottom-left" => Ok(Self::BottomLeft),
94 "bottom-middle" => Ok(Self::BottomMiddle),
95 "bottom-right" => Ok(Self::BottomRight),
96 _ => Err(StyleError::InvalidValue {
97 entity: record.entity.type_name.to_string(),
98 id: record.id,
99 attribute: "BoxAlignment",
100 value: value.to_owned(),
101 }),
102 }
103 }
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
108pub enum AnnotationType {
109 ContourLine,
111 Dimension,
113 Isobar,
115 Isolux,
117 Isotherm,
119 Leader,
121 Survey,
123 Symbol,
125 Text,
127 UserDefined,
129 NotDefined,
131}
132
133impl AnnotationType {
134 pub(crate) const fn as_ifc(self) -> &'static str {
135 match self {
136 Self::ContourLine => "CONTOURLINE",
137 Self::Dimension => "DIMENSION",
138 Self::Isobar => "ISOBAR",
139 Self::Isolux => "ISOLUX",
140 Self::Isotherm => "ISOTHERM",
141 Self::Leader => "LEADER",
142 Self::Survey => "SURVEY",
143 Self::Symbol => "SYMBOL",
144 Self::Text => "TEXT",
145 Self::UserDefined => "USERDEFINED",
146 Self::NotDefined => "NOTDEFINED",
147 }
148 }
149
150 fn parse(value: &str, record: Record<'_, '_>) -> StyleResult<Self> {
151 match value.to_ascii_uppercase().as_str() {
152 "CONTOURLINE" => Ok(Self::ContourLine),
153 "DIMENSION" => Ok(Self::Dimension),
154 "ISOBAR" => Ok(Self::Isobar),
155 "ISOLUX" => Ok(Self::Isolux),
156 "ISOTHERM" => Ok(Self::Isotherm),
157 "LEADER" => Ok(Self::Leader),
158 "SURVEY" => Ok(Self::Survey),
159 "SYMBOL" => Ok(Self::Symbol),
160 "TEXT" => Ok(Self::Text),
161 "USERDEFINED" => Ok(Self::UserDefined),
162 "NOTDEFINED" => Ok(Self::NotDefined),
163 _ => Err(StyleError::InvalidValue {
164 entity: record.entity.type_name.to_string(),
165 id: record.id,
166 attribute: "PredefinedType",
167 value: value.to_owned(),
168 }),
169 }
170 }
171}
172
173#[derive(Debug, Clone, Copy)]
175pub struct Annotation<'m, 's> {
176 record: Record<'m, 's>,
177}
178
179impl<'m, 's> Annotation<'m, 's> {
180 pub(crate) fn from_record(record: Record<'m, 's>) -> StyleResult<Self> {
181 Ok(Self { record })
182 }
183
184 #[must_use]
186 pub fn id(&self) -> EntityId {
187 self.record.id
188 }
189
190 pub fn global_id(&self) -> StyleResult<&'m str> {
192 self.record.required_text("GlobalId")
193 }
194
195 pub fn name(&self) -> StyleResult<Option<&'m str>> {
197 self.record.optional_text("Name")
198 }
199
200 pub fn description(&self) -> StyleResult<Option<&'m str>> {
202 self.record.optional_text("Description")
203 }
204
205 pub fn object_type(&self) -> StyleResult<Option<&'m str>> {
207 self.record.optional_text("ObjectType")
208 }
209
210 pub fn owner_history(&self) -> StyleResult<Option<EntityId>> {
212 self.record.optional_ref("OwnerHistory", "IfcOwnerHistory")
213 }
214
215 pub fn object_placement(&self) -> StyleResult<Option<EntityId>> {
217 self.record
218 .optional_ref("ObjectPlacement", "IfcObjectPlacement")
219 }
220
221 pub fn representation(&self) -> StyleResult<Option<EntityId>> {
223 self.record
224 .optional_ref("Representation", "IfcProductRepresentation")
225 }
226
227 pub fn predefined_type(&self) -> StyleResult<Option<AnnotationType>> {
229 self.record
230 .optional_enum("PredefinedType")?
231 .map(|value| AnnotationType::parse(value, self.record))
232 .transpose()
233 }
234}
235
236#[derive(Debug, Clone, Copy)]
238pub struct TextLiteral<'m, 's> {
239 record: Record<'m, 's>,
240}
241
242impl<'m, 's> TextLiteral<'m, 's> {
243 pub(crate) fn from_record(record: Record<'m, 's>) -> StyleResult<Self> {
244 Ok(Self { record })
245 }
246
247 #[must_use]
249 pub fn id(&self) -> EntityId {
250 self.record.id
251 }
252
253 pub fn literal(&self) -> StyleResult<&'m str> {
255 self.record.required_text("Literal")
256 }
257
258 pub fn placement(&self) -> StyleResult<EntityId> {
260 self.record.required_ref("Placement", "IfcPlacement")
261 }
262
263 pub fn path(&self) -> StyleResult<TextPath> {
265 TextPath::parse(self.record.required_enum("Path")?, self.record.id)
266 }
267}
268
269#[derive(Debug, Clone, Copy)]
271pub struct TextLiteralWithExtent<'m, 's> {
272 record: Record<'m, 's>,
273}
274
275impl<'m, 's> TextLiteralWithExtent<'m, 's> {
276 pub(crate) fn from_record(record: Record<'m, 's>) -> StyleResult<Self> {
277 Ok(Self { record })
278 }
279
280 #[must_use]
282 pub fn id(&self) -> EntityId {
283 self.record.id
284 }
285
286 pub fn literal(&self) -> StyleResult<&'m str> {
288 self.record.required_text("Literal")
289 }
290
291 pub fn placement(&self) -> StyleResult<EntityId> {
293 self.record.required_ref("Placement", "IfcPlacement")
294 }
295
296 pub fn path(&self) -> StyleResult<TextPath> {
298 TextPath::parse(self.record.required_enum("Path")?, self.record.id)
299 }
300
301 pub fn extent(&self) -> StyleResult<EntityId> {
303 self.record.required_ref("Extent", "IfcPlanarExtent")
304 }
305
306 pub fn box_alignment(&self) -> StyleResult<BoxAlignment> {
308 BoxAlignment::parse(self.record.required_text("BoxAlignment")?, self.record)
309 }
310}
311
312#[derive(Debug, Clone, Copy)]
314pub struct AnnotationFillArea<'m, 's> {
315 record: Record<'m, 's>,
316}
317
318impl<'m, 's> AnnotationFillArea<'m, 's> {
319 pub(crate) fn from_record(record: Record<'m, 's>) -> StyleResult<Self> {
320 Ok(Self { record })
321 }
322
323 #[must_use]
325 pub fn id(&self) -> EntityId {
326 self.record.id
327 }
328
329 pub fn outer_boundary(&self) -> StyleResult<EntityId> {
331 self.record.required_ref("OuterBoundary", "IfcCurve")
332 }
333
334 pub fn inner_boundaries(&self) -> StyleResult<Vec<EntityId>> {
336 self.record.optional_refs("InnerBoundaries", "IfcCurve")
337 }
338}