lib3mf_core/model/
resolver.rs

1use crate::archive::ArchiveReader;
2use crate::error::Result;
3use crate::model::{Model, Object, ResourceId};
4use crate::parser::model_parser::parse_model;
5use std::collections::HashMap;
6use std::io::Cursor;
7
8const ROOT_PATH: &str = "ROOT";
9const MAIN_MODEL_PART: &str = "3D/3dmodel.model";
10
11/// Resolves resources across multiple model parts in a 3MF package.
12pub struct PartResolver<'a, A: ArchiveReader> {
13    archive: &'a mut A,
14    models: HashMap<String, Model>,
15}
16
17impl<'a, A: ArchiveReader> PartResolver<'a, A> {
18    pub fn new(archive: &'a mut A, root_model: Model) -> Self {
19        let mut models = HashMap::new();
20        models.insert(ROOT_PATH.to_string(), root_model);
21        Self { archive, models }
22    }
23
24    pub fn resolve_object(
25        &mut self,
26        id: ResourceId,
27        path: Option<&str>,
28    ) -> Result<Option<(&Model, &Object)>> {
29        let model = self.get_or_load_model(path)?;
30        Ok(model.resources.get_object(id).map(|obj| (model, obj)))
31    }
32
33    pub fn resolve_base_materials(
34        &mut self,
35        id: ResourceId,
36        path: Option<&str>,
37    ) -> Result<Option<&crate::model::BaseMaterialsGroup>> {
38        let model = self.get_or_load_model(path)?;
39        Ok(model.resources.get_base_materials(id))
40    }
41
42    pub fn resolve_color_group(
43        &mut self,
44        id: ResourceId,
45        path: Option<&str>,
46    ) -> Result<Option<&crate::model::ColorGroup>> {
47        let model = self.get_or_load_model(path)?;
48        Ok(model.resources.get_color_group(id))
49    }
50
51    fn get_or_load_model(&mut self, path: Option<&str>) -> Result<&Model> {
52        let part_path = match path {
53            Some(p) => {
54                let p = p.trim_start_matches('/');
55                if p.is_empty() || p.eq_ignore_ascii_case(MAIN_MODEL_PART) {
56                    ROOT_PATH
57                } else {
58                    p
59                }
60            }
61            None => ROOT_PATH,
62        };
63
64        if !self.models.contains_key(part_path) {
65            let data = self.archive.read_entry(part_path).or_else(|_| {
66                let alt = format!("/{}", part_path);
67                self.archive.read_entry(&alt)
68            })?;
69
70            let model = parse_model(Cursor::new(data))?;
71            self.models.insert(part_path.to_string(), model);
72        }
73
74        Ok(self.models.get(part_path).unwrap())
75    }
76
77    pub fn get_root_model(&self) -> &Model {
78        self.models.get("ROOT").unwrap()
79    }
80
81    pub fn archive_mut(&mut self) -> &mut A {
82        self.archive
83    }
84}