use crate::lazy::{self, Bytes};
use crate::{parser, writer};
use ifc_model::{Codec, Model, ModelError};
use openbim_step::{is_step_file, OnMalformed, ParseOptions};
use std::io::Write;
use std::path::Path;
#[derive(Debug, Clone, Copy, Default)]
pub struct StepCodec;
impl StepCodec {
#[must_use]
pub const fn lenient() -> StepReader {
StepReader::new(ParseOptions::lenient())
}
#[must_use]
pub const fn with_options(options: ParseOptions) -> StepReader {
StepReader::new(options)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct StepReader {
options: ParseOptions,
eager: bool,
}
impl StepReader {
#[must_use]
pub const fn new(options: ParseOptions) -> Self {
Self {
options,
eager: false,
}
}
#[must_use]
pub const fn eager(mut self) -> Self {
self.eager = true;
self
}
pub unsafe fn read_path_mapped(&self, path: &Path) -> Result<Model, ModelError> {
let file = std::fs::File::open(path).map_err(|e| ModelError::Io(e.to_string()))?;
let map =
unsafe { memmap2::Mmap::map(&file) }.map_err(|e| ModelError::Io(e.to_string()))?;
if !is_step_file(&map) {
return Err(wrong_format());
}
if self.is_lazy() {
return lazy::read(Bytes::Mapped(map)).map_err(Into::into);
}
parser::parse(&map, self.options).map_err(Into::into)
}
fn is_lazy(&self) -> bool {
!self.eager && lazy::is_lazy(self.options)
}
#[must_use]
pub const fn on_malformed_record(mut self, policy: OnMalformed) -> Self {
self.options = self.options.on_malformed_record(policy);
self
}
#[must_use]
pub const fn options(&self) -> ParseOptions {
self.options
}
}
impl Codec for StepCodec {
fn name(&self) -> &'static str {
StepReader::new(ParseOptions::strict()).name()
}
fn extensions(&self) -> &'static [&'static str] {
StepReader::new(ParseOptions::strict()).extensions()
}
fn detect(&self, bytes: &[u8]) -> bool {
is_step_file(bytes)
}
fn read_bytes(&self, bytes: &[u8]) -> Result<Model, ModelError> {
StepReader::new(ParseOptions::strict()).read_bytes(bytes)
}
fn read_owned(&self, bytes: Vec<u8>) -> Result<Model, ModelError> {
StepReader::new(ParseOptions::strict()).read_owned(bytes)
}
fn write(&self, model: &Model, out: &mut dyn Write) -> Result<(), ModelError> {
writer::write(model, out).map_err(|e| ModelError::Write(e.to_string()))
}
fn read_path(&self, path: &Path) -> Result<Model, ModelError> {
StepReader::new(ParseOptions::strict()).read_path(path)
}
}
fn wrong_format() -> ModelError {
ModelError::WrongFormat {
expected: "STEP",
detail: "missing ISO-10303-21 magic".into(),
}
}
impl Codec for StepReader {
fn name(&self) -> &'static str {
"STEP"
}
fn extensions(&self) -> &'static [&'static str] {
&["ifc", "step", "stp"]
}
fn detect(&self, bytes: &[u8]) -> bool {
is_step_file(bytes)
}
fn read_bytes(&self, bytes: &[u8]) -> Result<Model, ModelError> {
if !is_step_file(bytes) {
return Err(wrong_format());
}
if self.is_lazy() {
return lazy::read(Bytes::Owned(bytes.to_vec())).map_err(Into::into);
}
parser::parse(bytes, self.options).map_err(Into::into)
}
fn read_owned(&self, bytes: Vec<u8>) -> Result<Model, ModelError> {
if !is_step_file(&bytes) {
return Err(wrong_format());
}
if self.is_lazy() {
return lazy::read(Bytes::Owned(bytes)).map_err(Into::into);
}
parser::parse(&bytes, self.options).map_err(Into::into)
}
fn write(&self, model: &Model, out: &mut dyn Write) -> Result<(), ModelError> {
writer::write(model, out).map_err(|e| ModelError::Write(e.to_string()))
}
fn read_path(&self, path: &Path) -> Result<Model, ModelError> {
let bytes = std::fs::read(path).map_err(|e| ModelError::Io(e.to_string()))?;
self.read_owned(bytes)
}
}