lib3mf_core/parser/
streaming.rs

1use crate::error::{Lib3mfError, Result};
2use crate::model::{BuildItem, ResourceId};
3use crate::parser::material_parser::{parse_base_materials, parse_color_group};
4use crate::parser::visitor::ModelVisitor;
5use crate::parser::xml_parser::{XmlParser, get_attribute, get_attribute_f32, get_attribute_u32};
6use glam::Mat4;
7use quick_xml::events::Event;
8use std::io::BufRead;
9
10/// Parses a 3MF model from an XML reader in a streaming fashion,
11/// emitting events to the provided visitor.
12pub fn parse_model_streaming<R: BufRead, V: ModelVisitor>(
13    reader: R,
14    visitor: &mut V,
15) -> Result<()> {
16    let mut parser = XmlParser::new(reader);
17
18    visitor.on_start_model()?;
19
20    loop {
21        match parser.read_next_event()? {
22            Event::Start(e) => {
23                match e.name().as_ref() {
24                    b"model" => {
25                        // Attributes like unit/lang handled here if needed?
26                    }
27                    b"metadata" => {
28                        let name = get_attribute(&e, b"name")
29                            .ok_or(Lib3mfError::Validation("Metadata missing name".to_string()))?
30                            .into_owned();
31                        let content = parser.read_text_content()?;
32                        visitor.on_metadata(&name, &content)?;
33                    }
34                    b"resources" => {
35                        visitor.on_start_resources()?;
36                        parse_resources_streaming(&mut parser, visitor)?;
37                        visitor.on_end_resources()?;
38                    }
39                    b"build" => {
40                        visitor.on_start_build()?;
41                        parse_build_streaming(&mut parser, visitor)?;
42                        visitor.on_end_build()?;
43                    }
44                    _ => {}
45                }
46            }
47            Event::Empty(e) => {
48                if e.name().as_ref() == b"metadata" {
49                    let name = get_attribute(&e, b"name")
50                        .ok_or(Lib3mfError::Validation("Metadata missing name".to_string()))?;
51                    visitor.on_metadata(name.as_ref(), "")?;
52                }
53            }
54            Event::End(e) if e.name().as_ref() == b"model" => break,
55            Event::Eof => break,
56            _ => {}
57        }
58    }
59
60    visitor.on_end_model()?;
61    Ok(())
62}
63
64fn parse_resources_streaming<R: BufRead, V: ModelVisitor>(
65    parser: &mut XmlParser<R>,
66    visitor: &mut V,
67) -> Result<()> {
68    loop {
69        match parser.read_next_event()? {
70            Event::Start(e) => {
71                let local_name = e.local_name();
72                match local_name.as_ref() {
73                    b"object" => {
74                        let id = ResourceId(get_attribute_u32(&e, b"id")?);
75                        parse_object_content_streaming(parser, visitor, id)?;
76                    }
77                    b"basematerials" => {
78                        let id = ResourceId(get_attribute_u32(&e, b"id")?);
79                        let group = parse_base_materials(parser, id)?;
80                        visitor.on_base_materials(id, &group)?;
81                    }
82                    b"colorgroup" => {
83                        let id = ResourceId(get_attribute_u32(&e, b"id")?);
84                        let group = parse_color_group(parser, id)?;
85                        visitor.on_color_group(id, &group)?;
86                    }
87                    _ => {}
88                }
89            }
90            Event::End(e) if e.name().as_ref() == b"resources" => break,
91            Event::Eof => {
92                return Err(Lib3mfError::Validation(
93                    "Unexpected EOF in resources".to_string(),
94                ));
95            }
96            _ => {}
97        }
98    }
99    Ok(())
100}
101
102fn parse_object_content_streaming<R: BufRead, V: ModelVisitor>(
103    parser: &mut XmlParser<R>,
104    visitor: &mut V,
105    object_id: ResourceId,
106) -> Result<()> {
107    loop {
108        match parser.read_next_event()? {
109            Event::Start(e) => match e.name().as_ref() {
110                b"mesh" => {
111                    visitor.on_start_mesh(object_id)?;
112                    parse_mesh_streaming(parser, visitor)?;
113                    visitor.on_end_mesh()?;
114                }
115                b"components" => {
116                    // Skipping components for now
117                }
118                _ => {}
119            },
120            Event::End(e) if e.name().as_ref() == b"object" => break,
121            Event::Eof => {
122                return Err(Lib3mfError::Validation(
123                    "Unexpected EOF in object".to_string(),
124                ));
125            }
126            _ => {}
127        }
128    }
129    Ok(())
130}
131
132fn parse_mesh_streaming<R: BufRead, V: ModelVisitor>(
133    parser: &mut XmlParser<R>,
134    visitor: &mut V,
135) -> Result<()> {
136    loop {
137        match parser.read_next_event()? {
138            Event::Start(e) => match e.name().as_ref() {
139                b"vertices" => parse_vertices_streaming(parser, visitor)?,
140                b"triangles" => parse_triangles_streaming(parser, visitor)?,
141                _ => {}
142            },
143            Event::End(e) if e.name().as_ref() == b"mesh" => break,
144            Event::Eof => {
145                return Err(Lib3mfError::Validation(
146                    "Unexpected EOF in mesh".to_string(),
147                ));
148            }
149            _ => {}
150        }
151    }
152    Ok(())
153}
154
155fn parse_vertices_streaming<R: BufRead, V: ModelVisitor>(
156    parser: &mut XmlParser<R>,
157    visitor: &mut V,
158) -> Result<()> {
159    loop {
160        match parser.read_next_event()? {
161            Event::Start(e) | Event::Empty(e) => {
162                if e.name().as_ref() == b"vertex" {
163                    let x = get_attribute_f32(&e, b"x")?;
164                    let y = get_attribute_f32(&e, b"y")?;
165                    let z = get_attribute_f32(&e, b"z")?;
166                    visitor.on_vertex(x, y, z)?;
167                }
168            }
169            Event::End(e) if e.name().as_ref() == b"vertices" => break,
170            Event::Eof => {
171                return Err(Lib3mfError::Validation(
172                    "Unexpected EOF in vertices".to_string(),
173                ));
174            }
175            _ => {}
176        }
177    }
178    Ok(())
179}
180
181fn parse_triangles_streaming<R: BufRead, V: ModelVisitor>(
182    parser: &mut XmlParser<R>,
183    visitor: &mut V,
184) -> Result<()> {
185    loop {
186        match parser.read_next_event()? {
187            Event::Start(e) | Event::Empty(e) => {
188                if e.name().as_ref() == b"triangle" {
189                    let v1 = get_attribute_u32(&e, b"v1")?;
190                    let v2 = get_attribute_u32(&e, b"v2")?;
191                    let v3 = get_attribute_u32(&e, b"v3")?;
192                    visitor.on_triangle(v1, v2, v3)?;
193                }
194            }
195            Event::End(e) if e.name().as_ref() == b"triangles" => break,
196            Event::Eof => {
197                return Err(Lib3mfError::Validation(
198                    "Unexpected EOF in triangles".to_string(),
199                ));
200            }
201            _ => {}
202        }
203    }
204    Ok(())
205}
206
207fn parse_build_streaming<R: BufRead, V: ModelVisitor>(
208    parser: &mut XmlParser<R>,
209    visitor: &mut V,
210) -> Result<()> {
211    loop {
212        match parser.read_next_event()? {
213            Event::Start(e) | Event::Empty(e) => {
214                if e.name().as_ref() == b"item" {
215                    let object_id = ResourceId(get_attribute_u32(&e, b"objectid")?);
216                    let item = BuildItem {
217                        object_id,
218                        transform: Mat4::IDENTITY,
219                        part_number: None,
220                        uuid: None,
221                        path: None,
222                    };
223                    visitor.on_build_item(&item)?;
224                }
225            }
226            Event::End(e) if e.name().as_ref() == b"build" => break,
227            Event::Eof => {
228                return Err(Lib3mfError::Validation(
229                    "Unexpected EOF in build".to_string(),
230                ));
231            }
232            _ => {}
233        }
234    }
235    Ok(())
236}