Skip to content

feat(geometry): native geometry-data export + Python (PyO3) binding - #1316

Merged
louistrue merged 6 commits into
mainfrom
feat/geometry-data-export
Jun 22, 2026
Merged

feat(geometry): native geometry-data export + Python (PyO3) binding#1316
louistrue merged 6 commits into
mainfrom
feat/geometry-data-export

Conversation

@louistrue

@louistrue louistrue commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

What

An analysis-oriented geometry-data export plus a native Python binding for the ifc-lite kernel.

  • rust/processing/src/geometry_export.rsbuild_geometry_data_export(&[MeshData], rtc_offset): per-entity geometry distinct from the render GLB:
    • IFC Z-up (reads MeshData before the wasm Y-up boundary)
    • absolute-world metres: vertex = position + origin + rtc_offset (per-element local frame + model RTC folded back; offset recorded in the output)
    • position-welded indexed triangles (1um) so closed-mesh consumers (volume / watertightness) work; the GLB's per-face duplication is avoided
    • occurrences only (geometry_class == 0), submeshes merged, keyed by IFC STEP id
    • serializes to the ifc-lite-geometry-data JSON contract
  • rust/python — PyO3 cdylib ifclite_geom (abi3-py39): geometry_data_buffers(bytes) (raw LE byte buffers for numpy.frombuffer) and geometry_data_json(bytes). Runs process_geometry natively (rayon, 256 MiB worker stack, GIL released).
  • .github/workflows/python-wheels.yml — per-platform abi3 wheels (manylinux x86_64/aarch64, macOS arm64/x86_64, Windows) via maturin-action; PyPI trusted publish on ifclite-geom-v* tags.

Why

Python consumers (compas_ifc and others) had no clean path to ifc-lite geometry: the CLI is Node + wasm + subprocess, and the wasm path can't run process_geometry (rayon + Instant::now). This is a direct in-process native path — no Node, no wasm, no GLB round-trip. Reading the kernel mesh directly, geometry comes out welded + Z-up + world-coords by construction (the IfcOpenShell frame), so volume/closedness work with no Y-up/recenter handling downstream.

Results (measured through the compas_ifc integration, separate repo)

  • 5-15x faster than parallel IfcOpenShell end-to-end (file -> per-entity geometry), comparable peak RSS.
  • 98.65% geometrically equivalent to IfcOpenShell across 22k entities (analytic-truth on synthetic shapes + an independent manifold3d referee on real ones); residual is two known wall-opening defects, tracked separately.

Notes

  • ifc-lite-python is excluded from the core workspace (it has its own wheel CI), so cargo test --workspace / clippy / cargo doc are unaffected and the root Cargo.lock carries no pyo3. It keeps its own Cargo.lock + path deps.
  • Native test rust/processing/tests/geometry_data_export_test.rs verifies welded / Z-up / absolute-world output (skips when the gitignored fixture is absent, per the repo convention).
  • The same contract can later feed wasm/JS consumers (browser viewer GLB bridge) on a separate track.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Python library for exporting IFC geometry data to JSON or buffer formats, including vertices, faces, element metadata, and spatial positioning information.
  • Chores

    • Set up CI/CD pipeline for automated building and publishing of Python packages to PyPI.

@vercel

vercel Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ifc-lite Ready Ready Preview, Comment Jun 22, 2026 10:28am
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
ifc-lite-dev Ignored Ignored Preview Jun 22, 2026 10:28am
ifc-lite-viewer-embed Ignored Ignored Jun 22, 2026 10:28am

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a geometry_export module to the Rust processing crate that builds welded, world-space triangle-soup exports (ExportedElement/GeometryDataExport) from IFC mesh data. A new rust/python crate wraps this via PyO3 as the ifclite_geom Python extension module exposing buffer and JSON entry points. A GitHub Actions workflow handles multi-platform wheel builds and PyPI publishing via OIDC.

Changes

ifclite-geom geometry export and Python bindings

Layer / File(s) Summary
Geometry export data contracts and module wiring
rust/processing/src/geometry_export.rs, rust/processing/src/lib.rs
Defines ExportedElement and GeometryDataExport Serde structs with coordinate-convention module docs and JSON serialization helpers; re-exports all three public symbols from the crate root.
build_geometry_data_export and vertex welding
rust/processing/src/geometry_export.rs
Implements occurrence filtering, world-space vertex transform (site rotation + rtc offset), per-element submesh merging, and weld_positions using a quantized grid to deduplicate vertices and drop degenerate triangles.
Geometry export integration tests
rust/processing/tests/geometry_data_export_test.rs
Adds geometry_data_export_is_welded_zup_world with an inline IFC4 unit-cube fixture, bbox/approx helpers, and assertions on metadata, welded vertex/face counts, world bounds, face validity, and JSON round-trip.
Python crate manifests and workspace exclusion
Cargo.toml, rust/python/Cargo.toml, rust/python/pyproject.toml
Adds the cdylib crate manifest (pyo3 0.22, abi3-py39, path dep on ifc-lite-processing), pyproject.toml (maturin backend, package metadata), and excludes rust/python from the root Cargo workspace.
PyO3 entry points: geometry_data_buffers and geometry_data_json
rust/python/src/lib.rs
Implements a worker-threaded run_export pipeline (8 MB stack, conditional site rotation), geometry_data_buffers returning a Python dict of bytes buffers via unsafe reinterpretation, geometry_data_json returning compact JSON, and the #[pymodule] initializer.
GitHub Actions wheel build and PyPI publish workflow
.github/workflows/python-wheels.yml
Adds a matrix build job for Ubuntu x86_64/aarch64, macOS x86_64/aarch64, Windows x64 via maturin-action, and a publish job gated on ifclite-geom-v* tags publishing merged artifacts to PyPI using OIDC trusted publishing.

Sequence Diagram

sequenceDiagram
  participant PyCaller as Python caller
  participant ifclite_geom as ifclite_geom (PyO3 module)
  participant WorkerThread as Worker thread (8 MB stack)
  participant ProcessGeom as process_geometry
  participant BuildExport as build_geometry_data_export

  PyCaller->>ifclite_geom: geometry_data_buffers(ifc_bytes) or geometry_data_json(ifc_bytes)
  ifclite_geom->>ifclite_geom: py.allow_threads
  ifclite_geom->>WorkerThread: spawn named thread
  WorkerThread->>ProcessGeom: process_geometry(ifc_bytes)
  ProcessGeom-->>WorkerThread: MeshData[], coordinate_space, rtc_offset
  WorkerThread->>BuildExport: build_geometry_data_export(meshes, rtc_offset, site_rotation?)
  BuildExport-->>WorkerThread: GeometryDataExport
  WorkerThread-->>ifclite_geom: Ok(GeometryDataExport)
  ifclite_geom-->>PyCaller: dict{up_axis, units, rtc_offset, element_count, elements{bytes}} OR JSON String
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested labels

geometry, rust

Poem

🐇 Hop, hop, a cube appears in Z-up space,
Welded vertices find their rightful place.
PyO3 bridges Rust to Python's call,
Maturin spins the wheels for one and all.
ifclite_geom ships on every OS,
The bunny cheers — geometry, no less! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: introducing native geometry-data export with a Python PyO3 binding, which is the core feature added across multiple files in this PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/geometry-data-export

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

…coords)

New ifc_lite_processing::geometry_export — a per-entity geometry dump for
analysis consumers (compas_ifc and others), distinct from the render GLB:
- IFC Z-up (reads MeshData before the wasm Y-up boundary)
- absolute-world metres: vertex = position + origin + rtc_offset (per-element
  local frame + model RTC offset folded back; offset recorded in the output)
- position-welded indexed triangles (1um) so closed-mesh consumers (volume,
  watertightness) work; the GLB's per-face duplication is avoided
- occurrences only (geometry_class==0); keyed by IFC STEP id; submeshes merged
- serializes to the ifc-lite-geometry-data JSON contract

Native test (synthetic corpus, skips if the gitignored fixture is absent):
cube welds to 8 corners/12 faces at world [0,0,0]-[1,1,1] Z-up, control at
[40..41], faces valid, JSON round-trips.
rust/python (cdylib `ifclite_geom`, abi3-py39): exposes geometry_data_json(bytes)
-> the ifc-lite-geometry-data JSON, calling process_geometry +
build_geometry_data_export natively (rayon, 256MiB worker stack, GIL released).
This is the Python path for compas_ifc and other consumers: no Node, no wasm,
no subprocess, no GLB round-trip. Geometry comes out welded, IFC Z-up,
absolute-world metres - so compas closed-mesh volume works (the GLB unweld bug
disappears) and coordinates match the IfcOpenShell frame.

Built with `maturin develop -m rust/python/Cargo.toml --release`.
- geometry_data_buffers(bytes): returns per-entity vertices/faces as raw LE byte
  buffers (f64 xyz / u32 tris) for numpy.frombuffer on the Python side, plus the
  existing geometry_data_json for debug/interchange. Dropping JSON lifted the
  geomean speedup vs IfcOpenShell from ~8.8x to ~11x (AC-20: 8.9x -> 15x).
- rust/python/pyproject.toml (maturin backend, abi3-py39, dist `ifclite-geom`).
- .github/workflows/python-wheels.yml: per-platform abi3 wheels (manylinux x86_64
  + aarch64, macOS arm64 + x86_64, Windows) via maturin-action; PyPI trusted
  publishing on `ifclite-geom-v*` tags. Verified `maturin build` locally (1.3MB).
The PyO3 cdylib has its own wheel CI (python-wheels.yml), so keep it OUT of the
core Rust lanes (cargo test --workspace, clippy -D, cargo doc --workspace):
make it a workspace `exclude` with explicit version + path deps and its own
Cargo.lock. Root Cargo.lock no longer carries pyo3.
@louistrue
louistrue force-pushed the feat/geometry-data-export branch from ecbf3e3 to d379b51 Compare June 22, 2026 10:04

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ecbf3e343a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rust/processing/src/geometry_export.rs Outdated
Comment on lines +85 to +87
p[0] as f64 + o[0] + rtc_offset[0],
p[1] as f64 + o[1] + rtc_offset[1],
p[2] as f64 + o[2] + rtc_offset[2],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reapply the site rotation before exporting world vertices

When process_geometry selects site_local for a translated IfcSite with a non-identity rotation, processor.rs:1562 passes site_transform into convert_mesh_to_site_local, which inverse-rotates both positions and mesh.origin (processor.rs:168-179). These lines only add the translation/RTC back, so those models are exported in site-local axes shifted to the world origin rather than the promised absolute IFC/world coordinates; reapply the site 3x3 transform, or avoid site-local rotation for this export path.

Useful? React with 👍 / 👎.


use ifc_lite_processing::{build_geometry_data_export, process_geometry};

const FIXTURE: &str = "../../tests/models/synthetic_box.ifc";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Register the fixture before relying on this test

I checked tests/models/manifest.json and repo-wide references, and tests/models/synthetic_box.ifc is neither committed nor fetchable via pnpm fixtures. In CI this read always falls into the skip path, so the new geometry-data exporter has no running regression coverage despite the assertions below; use an existing manifest fixture or add/upload this one so the test actually exercises the feature.

Useful? React with 👍 / 👎.

@@ -0,0 +1,186 @@
//! Analysis-ready geometry-data export.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add the required MPL header to new Rust sources

AGENTS.md/LICENSE_HEADER.md require the MPL-2.0 header at the top of every new Rust source file, but this new file starts with module docs; the same applies to the other new Rust source files in this commit, so license/header checks or release review will reject the change until the standard header precedes the docs.

Useful? React with 👍 / 👎.

- Reapply the IfcSite forward 3x3 rotation when the model is processed into the
  `site_local` axis frame, so exported vertices are true IFC world coordinates
  (world = R*(position+origin)+rtc) rather than site-local axes shifted to the
  origin. Common (identity-site) models are unaffected (R = None).
- Add the MPL-2.0 header to the new Rust sources (AGENTS.md/LICENSE_HEADER.md).
- Make the native test self-contained via an inline minimal IFC cube so it
  actually runs in CI (the previous gitignored fixture always hit the skip path).
- Linux wheels: `before-script-linux: rustup component add rust-src` so the
  manylinux container can rebuild std for the repo's global [unstable] build-std.
@louistrue

Copy link
Copy Markdown
Collaborator Author

Thanks for the review — all three addressed in 4b42d6b0, plus the Linux wheel build:

  • Site rotation (geometry_export.rs:87) — fixed. When the model is processed into the site_local frame, the export now reapplies the IfcSite forward 3x3 rotation, so vertices are true IFC world coordinates: world = R * (position + origin) + rtc_offset. model_rtc / raw_ifc (the common identity-site case) pass R = None and are unchanged. The PyO3 binding only passes the site transform when mesh_coordinate_space == "site_local".
  • Test fixture (geometry_data_export_test.rs) — fixed. The test no longer depends on the gitignored synthetic_box.ifc (which always skipped in CI). It now embeds a minimal inline IFC unit cube and asserts the export welds it to 8 corners / 12 triangles at world [0,0,0]..[1,1,1], so it actually runs as regression coverage.
  • MPL header — added the MPL-2.0 header to all new Rust sources (geometry_export.rs, rust/python/src/lib.rs, the test).

Also: the Linux wheel jobs were failing because the manylinux container's toolchain lacked rust-src for the repo's global [unstable] build-std; added before-script-linux: rustup component add rust-src.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
.github/workflows/python-wheels.yml (2)

35-35: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add persist-credentials: false for defense-in-depth.

The checkout action persists git credentials by default. While this workflow doesn't push to git, disabling credential persistence is good security hygiene and prevents accidental credential leakage if the workflow is later modified.

      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v4
+       with:
+         persist-credentials: false
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/python-wheels.yml at line 35, The actions/checkout action
is using the default configuration which persists git credentials, creating a
potential security risk. Add the `persist-credentials: false` parameter to the
actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 action configuration
to disable credential persistence and prevent accidental credential leakage if
the workflow is modified in the future.

Source: Linters/SAST tools


32-32: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Consider replacing macos-13 with macos-15-intel for longevity.

GitHub is deprecating macos-13 runners. While the static analysis warning is technically a false positive (macos-13 still works today), the runner is on a deprecation path. Using macos-15-intel provides a more future-proof x86_64 macOS build environment.

-          - { os: macos-13, target: x86_64 }
+          - { os: macos-15-intel, target: x86_64 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/python-wheels.yml at line 32, In the python-wheels.yml
workflow file, replace the deprecated macos-13 runner with macos-15-intel in the
matrix configuration. Locate the line containing `- { os: macos-13, target:
x86_64 }` and change macos-13 to macos-15-intel to use a non-deprecated,
future-proof x86_64 macOS runner that GitHub will continue to support.

Source: Linters/SAST tools

rust/python/src/lib.rs (1)

72-85: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Unsafe byte reinterpretation relies on little-endian targets.

The comment correctly notes that all wheel targets (x86_64, aarch64) are little-endian, making this transmute sound. The safety invariants are satisfied:

  • The Vec<[f64;3]> / Vec<[u32;3]> memory is contiguous
  • PyBytes::new_bound copies the data before export is dropped
  • All matrix targets in the CI workflow are LE architectures

The code is correct for current targets, but consider adding a compile-time assertion to guard against future big-endian targets:

🛡️ Optional: add compile-time LE assertion
#[cfg(not(target_endian = "little"))]
compile_error!("ifclite_geom byte buffers assume little-endian targets");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/python/src/lib.rs` around lines 72 - 85, Add a compile-time assertion to
guard against future big-endian targets, since the unsafe byte reinterpretation
of el.vertices and el.faces assumes little-endian architecture. Place a
compile_error! macro within a cfg attribute that triggers when the target is not
little-endian, positioned before the unsafe slice creation code that generates
vbytes and fbytes to ensure this architectural requirement is enforced at
compile time.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rust/processing/src/geometry_export.rs`:
- Around line 96-99: Validate that the positions buffer length is divisible by 3
before processing with chunks_exact(3) in the vertex parsing logic.
Additionally, add bounds checking when remapping faces to use vertex indices
from the parsed positions, skipping or handling any invalid triangle references
instead of using unchecked indexing. Apply these validation checks consistently
across all affected locations where chunks_exact(3) is used for position parsing
and face remapping occurs (the position parsing near chunks_exact(3) and the
subsequent face index access operations).

In `@rust/python/src/lib.rs`:
- Around line 28-47: The run_export function attempts to handle thread panics
using .join().map_err(), but the workspace's release profile configuration has
panic set to 'abort', which causes panics to terminate the entire process rather
than unwind, making the error handler unreachable. Add an explicit
[profile.release] section to the rust/python/Cargo.toml file that overrides the
workspace setting by configuring panic to "unwind" instead of "abort", which
will allow panics in the spawned geometry worker thread to be caught and
properly handled by the .join().map_err() error handler.

---

Nitpick comments:
In @.github/workflows/python-wheels.yml:
- Line 35: The actions/checkout action is using the default configuration which
persists git credentials, creating a potential security risk. Add the
`persist-credentials: false` parameter to the
actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 action configuration
to disable credential persistence and prevent accidental credential leakage if
the workflow is modified in the future.
- Line 32: In the python-wheels.yml workflow file, replace the deprecated
macos-13 runner with macos-15-intel in the matrix configuration. Locate the line
containing `- { os: macos-13, target: x86_64 }` and change macos-13 to
macos-15-intel to use a non-deprecated, future-proof x86_64 macOS runner that
GitHub will continue to support.

In `@rust/python/src/lib.rs`:
- Around line 72-85: Add a compile-time assertion to guard against future
big-endian targets, since the unsafe byte reinterpretation of el.vertices and
el.faces assumes little-endian architecture. Place a compile_error! macro within
a cfg attribute that triggers when the target is not little-endian, positioned
before the unsafe slice creation code that generates vbytes and fbytes to ensure
this architectural requirement is enforced at compile time.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9b3b0e90-550b-491e-b7d4-aa540ccafdc7

📥 Commits

Reviewing files that changed from the base of the PR and between cd466b9 and 4b42d6b.

⛔ Files ignored due to path filters (1)
  • rust/python/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • .github/workflows/python-wheels.yml
  • Cargo.toml
  • rust/processing/src/geometry_export.rs
  • rust/processing/src/lib.rs
  • rust/processing/tests/geometry_data_export_test.rs
  • rust/python/Cargo.toml
  • rust/python/pyproject.toml
  • rust/python/src/lib.rs

Comment on lines +96 to +99
let verts: Vec<[f64; 3]> = m
.positions
.chunks_exact(3)
.map(|p| {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate triangle buffer integrity before face remap to avoid panic.

chunks_exact(3) drops trailing scalars, and later remap uses unchecked indexing from face indices. If any malformed mesh slips through, export can panic on out-of-bounds. Add tri-alignment checks and safe remap access (skip invalid triangles).

💡 Proposed fix
@@
-        if m.geometry_class != 0 || m.indices.is_empty() {
+        if m.geometry_class != 0
+            || m.indices.is_empty()
+            || m.positions.len() % 3 != 0
+            || m.indices.len() % 3 != 0
+        {
             continue;
         }
@@
-    for f in faces {
-        let (a, b, c) = (
-            remap[f[0] as usize],
-            remap[f[1] as usize],
-            remap[f[2] as usize],
-        );
+    for f in faces {
+        let (Some(&a), Some(&b), Some(&c)) = (
+            remap.get(f[0] as usize),
+            remap.get(f[1] as usize),
+            remap.get(f[2] as usize),
+        ) else {
+            continue;
+        };
         if a != b && b != c && a != c {
             out_faces.push([a, b, c]);
         }
     }

Also applies to: 130-133, 185-190

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/processing/src/geometry_export.rs` around lines 96 - 99, Validate that
the positions buffer length is divisible by 3 before processing with
chunks_exact(3) in the vertex parsing logic. Additionally, add bounds checking
when remapping faces to use vertex indices from the parsed positions, skipping
or handling any invalid triangle references instead of using unchecked indexing.
Apply these validation checks consistently across all affected locations where
chunks_exact(3) is used for position parsing and face remapping occurs (the
position parsing near chunks_exact(3) and the subsequent face index access
operations).

Comment thread rust/python/src/lib.rs
Comment on lines +28 to +47
fn run_export(ifc_bytes: Vec<u8>) -> Result<GeometryDataExport, String> {
std::thread::Builder::new()
.stack_size(GEOMETRY_STACK_BYTES)
.name("ifclite-geometry".into())
.spawn(move || {
let result = process_geometry(&ifc_bytes);
let rtc = result.metadata.coordinate_info.origin_shift;
// Reapply the IfcSite rotation only in the site-local axis frame;
// model_rtc / raw_ifc keep true IFC world axes (R = identity).
let site_rotation = if result.mesh_coordinate_space.as_deref() == Some("site_local") {
result.site_transform.as_deref()
} else {
None
};
build_geometry_data_export(&result.meshes, rtc, site_rotation)
})
.map_err(|e| format!("spawn failed: {e}"))?
.join()
.map_err(|_| "geometry worker panicked".to_string())
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if rust/python/Cargo.toml has a release profile with panic setting
echo "=== Checking rust/python/Cargo.toml for panic profile ==="
cat rust/python/Cargo.toml

echo ""
echo "=== Checking root Cargo.toml for panic profile ==="
rg -n '\[profile' -A 5 Cargo.toml || echo "No profile section found in root"

echo ""
echo "=== Checking if there's a rust-toolchain or workspace profile that applies ==="
cat rust-toolchain.toml 2>/dev/null || echo "No rust-toolchain.toml"

Repository: LTplus-AG/ifc-lite

Length of output: 1878


Add explicit panic profile to rust/python to ensure error recovery.

The rust/python crate inherits the workspace's [profile.release] with panic = 'abort', but the run_export function (lines 45–46) attempts to handle thread panics with .join().map_err(). With panic = 'abort', panics will terminate the entire Python process rather than unwind, making the error handler unreachable and crashing the interpreter.

Add to rust/python/Cargo.toml:

[profile.release]
panic = "unwind"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/python/src/lib.rs` around lines 28 - 47, The run_export function
attempts to handle thread panics using .join().map_err(), but the workspace's
release profile configuration has panic set to 'abort', which causes panics to
terminate the entire process rather than unwind, making the error handler
unreachable. Add an explicit [profile.release] section to the
rust/python/Cargo.toml file that overrides the workspace setting by configuring
panic to "unwind" instead of "abort", which will allow panics in the spawned
geometry worker thread to be caught and properly handled by the
.join().map_err() error handler.

Source: Coding guidelines

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant