bimifc-parser 0.3.0

High-performance IFC parser with IFC4 (STEP) and IFC5 (IFCX) support
Documentation
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

//! BIMIFC Parser - High-performance IFC parser
//!
//! This crate provides a fast, memory-efficient parser for IFC (STEP) files.
//! It implements the traits defined in `bimifc-model` for a clean abstraction.
//!
//! # Features
//!
//! - **Fast tokenization** using `nom` combinators
//! - **SIMD-accelerated scanning** using `memchr`
//! - **Lazy entity decoding** - only parse entities when needed
//! - **Arc-based caching** - efficient memory sharing
//! - **Progress reporting** for large files
//!
//! # Example
//!
//! ```ignore
//! use bimifc_parser::StepParser;
//! use bimifc_model::IfcParser;
//!
//! let parser = StepParser::new();
//! let model = parser.parse(ifc_content)?;
//!
//! // Access entities
//! let walls = model.resolver().find_by_type_name("IFCWALL");
//! println!("Found {} walls", walls.len());
//! ```

mod decoder;
pub mod ifcx;
pub mod lighting;
mod model;
mod properties;
mod resolver;
mod scanner;
mod spatial;
mod tokenizer;
mod units;

pub use decoder::EntityDecoder;
pub use ifcx::{is_ifcx_format, IfcxGeometry, IfcxModel};
pub use lighting::{
    export_to_json, extract_lighting_data, goniometric_to_eulumdat, goniometric_to_ldt,
    light_source_to_eulumdat, light_source_to_ldt, DistributionPlane, LightDistributionData,
    LightFixtureData, LightSourceData, LightingExport,
};
pub use model::ParsedModel;
pub use scanner::EntityScanner;
pub use tokenizer::{parse_entity, Token};

use bimifc_model::{IfcModel, IfcParser, ProgressCallback, Result};
use std::sync::Arc;

/// Main STEP/IFC parser implementing `IfcParser` trait
///
/// This is the entry point for parsing IFC files. It creates a `ParsedModel`
/// that provides access to all IFC data through the trait interfaces.
#[derive(Default)]
pub struct StepParser {
    /// Whether to build spatial tree during parsing
    pub build_spatial_tree: bool,
    /// Whether to extract properties during parsing
    pub extract_properties: bool,
}

impl StepParser {
    /// Create a new parser with default settings
    pub fn new() -> Self {
        Self {
            build_spatial_tree: true,
            extract_properties: true,
        }
    }

    /// Create a parser optimized for geometry-only access
    pub fn geometry_only() -> Self {
        Self {
            build_spatial_tree: false,
            extract_properties: false,
        }
    }

    /// Set whether to build spatial tree
    pub fn with_spatial_tree(mut self, enabled: bool) -> Self {
        self.build_spatial_tree = enabled;
        self
    }

    /// Set whether to extract properties
    pub fn with_properties(mut self, enabled: bool) -> Self {
        self.extract_properties = enabled;
        self
    }
}

impl IfcParser for StepParser {
    fn parse(&self, content: &str) -> Result<Arc<dyn IfcModel>> {
        ParsedModel::parse(content, self.build_spatial_tree, self.extract_properties)
            .map(|m| Arc::new(m) as Arc<dyn IfcModel>)
    }

    fn parse_with_progress(
        &self,
        content: &str,
        on_progress: ProgressCallback,
    ) -> Result<Arc<dyn IfcModel>> {
        ParsedModel::parse_with_progress(
            content,
            self.build_spatial_tree,
            self.extract_properties,
            on_progress,
        )
        .map(|m| Arc::new(m) as Arc<dyn IfcModel>)
    }
}

/// Quick parse function for simple use cases
pub fn parse(content: &str) -> Result<Arc<dyn IfcModel>> {
    StepParser::new().parse(content)
}

/// Parse with progress reporting
pub fn parse_with_progress(
    content: &str,
    on_progress: impl Fn(&str, f32) + Send + 'static,
) -> Result<Arc<dyn IfcModel>> {
    StepParser::new().parse_with_progress(content, Box::new(on_progress))
}

/// Auto-detect format and parse (IFC4 STEP or IFC5 IFCX)
///
/// This is the recommended entry point for parsing IFC files when
/// the format is not known in advance.
pub fn parse_auto(content: &str) -> Result<Arc<dyn IfcModel>> {
    if is_ifcx_format(content) {
        IfcxModel::parse(content).map(|m| Arc::new(m) as Arc<dyn IfcModel>)
    } else {
        parse(content)
    }
}

/// Unified parser that auto-detects format
#[derive(Default)]
pub struct UnifiedParser {
    /// Settings for STEP parsing
    pub step_settings: StepParser,
}

impl UnifiedParser {
    pub fn new() -> Self {
        Self::default()
    }
}

impl IfcParser for UnifiedParser {
    fn parse(&self, content: &str) -> Result<Arc<dyn IfcModel>> {
        if is_ifcx_format(content) {
            IfcxModel::parse(content).map(|m| Arc::new(m) as Arc<dyn IfcModel>)
        } else {
            self.step_settings.parse(content)
        }
    }

    fn parse_with_progress(
        &self,
        content: &str,
        on_progress: ProgressCallback,
    ) -> Result<Arc<dyn IfcModel>> {
        if is_ifcx_format(content) {
            on_progress("Parsing IFCX JSON", 0.0);
            let result = IfcxModel::parse(content).map(|m| Arc::new(m) as Arc<dyn IfcModel>);
            on_progress("Done", 1.0);
            result
        } else {
            self.step_settings.parse_with_progress(content, on_progress)
        }
    }
}