Skip to main content

ifc_xml/
codec.rs

1//! Codec state and the shared model-codec adapter.
2
3use crate::{reader, writer, XmlProfile};
4use ifc_model::{Codec, Model, ModelError};
5
6/// The ifcXML codec.
7///
8/// Construct with [`XmlCodec::default`] for the lossless compatibility dialect,
9/// [`XmlCodec::strict`] for an exact namespace/release profile, or
10/// [`XmlCodec::with_schema_and_profile`] for strict output with schema-correct
11/// attribute names.
12#[derive(Default)]
13pub struct XmlCodec {
14    profile: Option<XmlProfile>,
15    #[cfg(feature = "schema")]
16    schema: Option<std::sync::Arc<ifc_schema::Schema>>,
17}
18
19impl XmlCodec {
20    /// A codec that enforces one exact ifcXML release namespace and schema token.
21    #[must_use]
22    pub const fn strict(profile: XmlProfile) -> Self {
23        Self {
24            profile: Some(profile),
25            #[cfg(feature = "schema")]
26            schema: None,
27        }
28    }
29
30    /// The strict release profile, or `None` for compatibility mode.
31    #[must_use]
32    pub const fn profile(&self) -> Option<XmlProfile> {
33        self.profile
34    }
35
36    /// A codec that emits schema-correct attribute names.
37    #[cfg(feature = "schema")]
38    #[must_use]
39    pub fn with_schema(schema: std::sync::Arc<ifc_schema::Schema>) -> Self {
40        Self {
41            profile: None,
42            schema: Some(schema),
43        }
44    }
45
46    /// A strict release-profile codec with schema-backed attribute names.
47    #[cfg(feature = "schema")]
48    #[must_use]
49    pub fn with_schema_and_profile(
50        schema: std::sync::Arc<ifc_schema::Schema>,
51        profile: XmlProfile,
52    ) -> Self {
53        Self {
54            profile: Some(profile),
55            schema: Some(schema),
56        }
57    }
58
59    /// The schema in use, if any.
60    #[cfg(feature = "schema")]
61    #[must_use]
62    pub fn schema(&self) -> Option<&ifc_schema::Schema> {
63        self.schema.as_deref()
64    }
65}
66
67impl Codec for XmlCodec {
68    fn name(&self) -> &'static str {
69        "ifcXML"
70    }
71
72    fn extensions(&self) -> &'static [&'static str] {
73        &["ifcxml", "xml"]
74    }
75
76    fn detect(&self, bytes: &[u8]) -> bool {
77        reader::looks_like_xml(bytes)
78    }
79
80    fn read_bytes(&self, bytes: &[u8]) -> Result<Model, ModelError> {
81        reader::read(self, bytes).map_err(|error| ModelError::Syntax {
82            offset: 0,
83            detail: error.to_string(),
84        })
85    }
86
87    fn write(&self, model: &Model, out: &mut dyn std::io::Write) -> Result<(), ModelError> {
88        let bytes =
89            writer::write(self, model).map_err(|error| ModelError::Write(error.to_string()))?;
90        out.write_all(&bytes)
91            .map_err(|error| ModelError::Io(error.to_string()))
92    }
93}