use crate::{reader, writer, XmlProfile};
use ifc_model::{Codec, Model, ModelError};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[non_exhaustive]
pub enum XmlLayout {
#[default]
Native,
Xsd,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[non_exhaustive]
pub enum SchemaReading {
#[default]
Strict,
Lenient,
}
#[derive(Debug, Clone, Default)]
pub struct XmlCodec {
profile: Option<XmlProfile>,
layout: XmlLayout,
reading: SchemaReading,
#[cfg(feature = "schema")]
schema: Option<std::sync::Arc<ifc_schema::Schema>>,
}
impl XmlCodec {
#[must_use]
pub const fn strict(profile: XmlProfile) -> Self {
Self {
profile: Some(profile),
layout: XmlLayout::Native,
reading: SchemaReading::Strict,
#[cfg(feature = "schema")]
schema: None,
}
}
#[must_use]
pub const fn profile(&self) -> Option<XmlProfile> {
self.profile
}
#[must_use]
pub const fn layout(&self) -> XmlLayout {
self.layout
}
#[must_use]
pub const fn reading(&self) -> SchemaReading {
match self.layout {
XmlLayout::Xsd => SchemaReading::Strict,
XmlLayout::Native => self.reading,
}
}
#[must_use]
pub const fn with_reading(mut self, reading: SchemaReading) -> Self {
self.reading = reading;
self
}
#[cfg(feature = "schema")]
#[must_use]
pub fn with_schema(schema: std::sync::Arc<ifc_schema::Schema>) -> Self {
Self {
profile: None,
layout: XmlLayout::Native,
reading: SchemaReading::Strict,
schema: Some(schema),
}
}
#[cfg(feature = "schema")]
#[must_use]
pub fn with_schema_and_profile(
schema: std::sync::Arc<ifc_schema::Schema>,
profile: XmlProfile,
) -> Self {
Self {
profile: Some(profile),
layout: XmlLayout::Native,
reading: SchemaReading::Strict,
schema: Some(schema),
}
}
#[cfg(feature = "schema")]
#[must_use]
pub fn xsd(schema: std::sync::Arc<ifc_schema::Schema>, profile: XmlProfile) -> Self {
Self {
profile: Some(profile),
layout: XmlLayout::Xsd,
reading: SchemaReading::Strict,
schema: Some(schema),
}
}
#[cfg(feature = "schema")]
#[must_use]
pub fn schema(&self) -> Option<&ifc_schema::Schema> {
self.schema.as_deref()
}
#[cfg(feature = "schema")]
pub(crate) fn strict_schema(&self) -> Option<&ifc_schema::Schema> {
match self.reading {
SchemaReading::Strict => self.schema(),
SchemaReading::Lenient => None,
}
}
}
impl Codec for XmlCodec {
fn name(&self) -> &'static str {
"ifcXML"
}
fn extensions(&self) -> &'static [&'static str] {
&["ifcxml", "xml"]
}
fn detect(&self, bytes: &[u8]) -> bool {
reader::looks_like_xml(bytes)
}
fn read_bytes(&self, bytes: &[u8]) -> Result<Model, ModelError> {
reader::read(self, bytes).map_err(|error| ModelError::Syntax {
offset: 0,
detail: error.to_string(),
})
}
fn write(&self, model: &Model, out: &mut dyn std::io::Write) -> Result<(), ModelError> {
let bytes =
writer::write(self, model).map_err(|error| ModelError::Write(error.to_string()))?;
out.write_all(&bytes)
.map_err(|error| ModelError::Io(error.to_string()))
}
}