icadkit 0.3.5__tar.gz → 0.3.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. {icadkit-0.3.5 → icadkit-0.3.8}/Cargo.lock +4 -4
  2. {icadkit-0.3.5 → icadkit-0.3.8}/Cargo.toml +3 -2
  3. {icadkit-0.3.5 → icadkit-0.3.8}/LICENSES/rust-dependencies.txt +1 -1
  4. {icadkit-0.3.5 → icadkit-0.3.8}/PKG-INFO +52 -27
  5. {icadkit-0.3.5 → icadkit-0.3.8}/README.md +50 -25
  6. {icadkit-0.3.5 → icadkit-0.3.8}/THIRD_PARTY_NOTICES.md +7 -5
  7. {icadkit-0.3.5 → icadkit-0.3.8}/corpus/public.jsonl +8 -8
  8. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-core/LICENSE +9 -7
  9. icadkit-0.3.8/crates/icad-core/build.rs +49 -0
  10. icadkit-0.3.8/crates/icad-core/src/container.rs +311 -0
  11. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-core/src/document.rs +13 -0
  12. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-core/src/lib.rs +7 -5
  13. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-core/src/native.rs +93 -2
  14. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-core/src/parts.rs +51 -0
  15. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-core/src/reader.rs +14 -0
  16. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-core/src/trailer.rs +294 -0
  17. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-core/src/views.rs +92 -0
  18. icadkit-0.3.8/crates/icad-core/tests/container.rs +203 -0
  19. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-core/tests/resources.rs +32 -8
  20. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-python/LICENSE +9 -7
  21. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-python/src/brep.rs +19 -0
  22. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-python/src/document.rs +206 -33
  23. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-python/src/lib.rs +1 -0
  24. {icadkit-0.3.5 → icadkit-0.3.8}/docs/api.md +54 -2
  25. icadkit-0.3.8/docs/changelog.md +485 -0
  26. {icadkit-0.3.5 → icadkit-0.3.8}/docs/csg.md +17 -3
  27. {icadkit-0.3.5 → icadkit-0.3.8}/docs/drawing.md +82 -6
  28. {icadkit-0.3.5 → icadkit-0.3.8}/docs/native.md +3 -1
  29. {icadkit-0.3.5 → icadkit-0.3.8}/docs/parts.md +20 -0
  30. {icadkit-0.3.5 → icadkit-0.3.8}/docs/preview.md +5 -3
  31. {icadkit-0.3.5 → icadkit-0.3.8}/docs/roadmap.md +98 -3
  32. {icadkit-0.3.5 → icadkit-0.3.8}/docs/saved-bodies.md +36 -8
  33. icadkit-0.3.8/docs/shapes.md +103 -0
  34. {icadkit-0.3.5 → icadkit-0.3.8}/docs/support.md +23 -8
  35. {icadkit-0.3.5 → icadkit-0.3.8}/docs/trailer.md +53 -3
  36. icadkit-0.3.8/docs/writing.md +189 -0
  37. {icadkit-0.3.5 → icadkit-0.3.8}/pyproject.toml +3 -1
  38. icadkit-0.3.8/scripts/bump_version.py +132 -0
  39. {icadkit-0.3.5 → icadkit-0.3.8}/scripts/check_license.py +54 -11
  40. icadkit-0.3.8/scripts/test_versions.py +229 -0
  41. {icadkit-0.3.5 → icadkit-0.3.8}/scripts/verify_artifacts.py +20 -22
  42. {icadkit-0.3.5 → icadkit-0.3.8}/scripts/verify_corpus.py +1 -0
  43. {icadkit-0.3.5 → icadkit-0.3.8}/scripts/verify_install.py +1 -7
  44. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/__init__.py +57 -1
  45. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/_core.pyi +42 -2
  46. icadkit-0.3.8/src/icadkit/_csg_occt.py +497 -0
  47. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/_preview_bridge.py +42 -2
  48. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/_saved_occt.py +134 -39
  49. icadkit-0.3.8/src/icadkit/_shape_occt.py +71 -0
  50. icadkit-0.3.8/src/icadkit/container.py +137 -0
  51. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/csg.py +90 -19
  52. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/document.py +20 -0
  53. icadkit-0.3.8/src/icadkit/drawing.py +1104 -0
  54. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/parts.py +101 -0
  55. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/preview.py +3 -9
  56. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/saved.py +40 -27
  57. icadkit-0.3.8/src/icadkit/shape.py +693 -0
  58. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/trailer.py +81 -7
  59. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/viewer.py +6 -6
  60. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/views.py +24 -0
  61. icadkit-0.3.8/src/icadkit/write.py +1750 -0
  62. icadkit-0.3.8/tests/drawing_fixtures.py +203 -0
  63. {icadkit-0.3.5 → icadkit-0.3.8}/tests/fixture_builders.py +2 -0
  64. {icadkit-0.3.5 → icadkit-0.3.8}/tests/saved_fixtures.py +156 -0
  65. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_api.py +2 -1
  66. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_cli.py +1 -1
  67. icadkit-0.3.8/tests/test_container.py +77 -0
  68. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_csg.py +143 -6
  69. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_distribution.py +4 -2
  70. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_drawing.py +266 -3
  71. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_geometry.py +19 -8
  72. icadkit-0.3.8/tests/test_parts_material.py +116 -0
  73. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_preview.py +2 -10
  74. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_saved.py +83 -6
  75. icadkit-0.3.8/tests/test_shape.py +376 -0
  76. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_trailer.py +147 -0
  77. icadkit-0.3.8/tests/test_write.py +722 -0
  78. {icadkit-0.3.5 → icadkit-0.3.8}/tests/trailer_fixtures.py +16 -8
  79. {icadkit-0.3.5 → icadkit-0.3.8}/uv.lock +8 -8
  80. icadkit-0.3.5/docs/changelog.md +0 -233
  81. icadkit-0.3.5/src/icadkit/_csg_occt.py +0 -288
  82. icadkit-0.3.5/src/icadkit/drawing.py +0 -304
  83. icadkit-0.3.5/tests/drawing_fixtures.py +0 -94
  84. {icadkit-0.3.5 → icadkit-0.3.8}/COMMERCIAL-LICENSE.md +0 -0
  85. {icadkit-0.3.5 → icadkit-0.3.8}/LICENSE +0 -0
  86. {icadkit-0.3.5 → icadkit-0.3.8}/LICENSES/Apache-2.0.txt +0 -0
  87. {icadkit-0.3.5 → icadkit-0.3.8}/LICENSES/PolyForm-Noncommercial-1.0.0.md +0 -0
  88. {icadkit-0.3.5 → icadkit-0.3.8}/LICENSES/parasolid-core-MIT.txt +0 -0
  89. {icadkit-0.3.5 → icadkit-0.3.8}/corpus/README.md +0 -0
  90. {icadkit-0.3.5 → icadkit-0.3.8}/corpus/generate.py +0 -0
  91. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-core/Cargo.toml +0 -0
  92. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-core/src/compression.rs +0 -0
  93. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-core/src/error.rs +0 -0
  94. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-core/src/header.rs +0 -0
  95. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-core/src/limits.rs +0 -0
  96. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-core/src/parasolid.rs +0 -0
  97. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-core/src/provenance.rs +0 -0
  98. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-core/src/record.rs +0 -0
  99. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-core/src/resource.rs +0 -0
  100. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-core/src/schema.rs +0 -0
  101. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-core/tests/header.rs +0 -0
  102. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-python/Cargo.toml +0 -0
  103. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-python/src/geometry.rs +0 -0
  104. {icadkit-0.3.5 → icadkit-0.3.8}/crates/icad-python/src/schema.rs +0 -0
  105. {icadkit-0.3.5 → icadkit-0.3.8}/docs/license.md +0 -0
  106. {icadkit-0.3.5 → icadkit-0.3.8}/docs/parameters.md +0 -0
  107. {icadkit-0.3.5 → icadkit-0.3.8}/docs/references.md +0 -0
  108. {icadkit-0.3.5 → icadkit-0.3.8}/docs/viewer.md +0 -0
  109. {icadkit-0.3.5 → icadkit-0.3.8}/scripts/check_public_tree.py +0 -0
  110. {icadkit-0.3.5 → icadkit-0.3.8}/scripts/test_publication.py +0 -0
  111. {icadkit-0.3.5 → icadkit-0.3.8}/scripts/verify_native_corpus.py +0 -0
  112. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/__main__.py +0 -0
  113. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/_viewer/index.html +0 -0
  114. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/_viewer/viewer.css +0 -0
  115. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/_viewer/viewer.js +0 -0
  116. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/_viewer_io.py +0 -0
  117. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/api.py +0 -0
  118. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/cli.py +0 -0
  119. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/errors.py +0 -0
  120. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/geometry.py +0 -0
  121. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/models.py +0 -0
  122. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/native.py +0 -0
  123. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/parameters.py +0 -0
  124. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/py.typed +0 -0
  125. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/references.py +0 -0
  126. {icadkit-0.3.5 → icadkit-0.3.8}/src/icadkit/schema.py +0 -0
  127. {icadkit-0.3.5 → icadkit-0.3.8}/tests/conftest.py +0 -0
  128. {icadkit-0.3.5 → icadkit-0.3.8}/tests/geometry_fixtures.py +0 -0
  129. {icadkit-0.3.5 → icadkit-0.3.8}/tests/parameter_fixtures.py +0 -0
  130. {icadkit-0.3.5 → icadkit-0.3.8}/tests/part_fixtures.py +0 -0
  131. {icadkit-0.3.5 → icadkit-0.3.8}/tests/preview_fixtures.py +0 -0
  132. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_corpus.py +0 -0
  133. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_extrusion.py +0 -0
  134. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_followup_parts.py +0 -0
  135. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_inspect.py +0 -0
  136. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_legacy_parts.py +0 -0
  137. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_mirrors.py +0 -0
  138. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_native.py +0 -0
  139. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_native_corpus.py +0 -0
  140. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_native_round.py +0 -0
  141. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_parameters.py +0 -0
  142. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_part_profiles.py +0 -0
  143. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_part_semantics.py +0 -0
  144. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_parts.py +0 -0
  145. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_profile_shapes.py +0 -0
  146. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_references.py +0 -0
  147. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_resource_cli.py +0 -0
  148. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_resources.py +0 -0
  149. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_v7_parts.py +0 -0
  150. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_v8l4_boundary.py +0 -0
  151. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_version_geometry.py +0 -0
  152. {icadkit-0.3.5 → icadkit-0.3.8}/tests/test_viewer.py +0 -0
  153. {icadkit-0.3.5 → icadkit-0.3.8}/tests/viewer_assembly_browser.mjs +0 -0
  154. {icadkit-0.3.5 → icadkit-0.3.8}/tests/viewer_browser.mjs +0 -0
  155. {icadkit-0.3.5 → icadkit-0.3.8}/tests/viewer_mirror_browser.mjs +0 -0
@@ -89,7 +89,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
89
89
 
90
90
  [[package]]
91
91
  name = "icad-core"
92
- version = "0.3.5"
92
+ version = "0.3.8"
93
93
  dependencies = [
94
94
  "flate2",
95
95
  "parasolid-core",
@@ -99,7 +99,7 @@ dependencies = [
99
99
 
100
100
  [[package]]
101
101
  name = "icad-python"
102
- version = "0.3.5"
102
+ version = "0.3.8"
103
103
  dependencies = [
104
104
  "icad-core",
105
105
  "parasolid-core",
@@ -142,9 +142,9 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
142
142
 
143
143
  [[package]]
144
144
  name = "parasolid-core"
145
- version = "0.3.5"
145
+ version = "0.3.7"
146
146
  source = "registry+https://github.com/rust-lang/crates.io-index"
147
- checksum = "ea19ba26c4ea0217f3f81dc5a5479239f92d43fc08bd6754d9f9f5ad74cf2c0a"
147
+ checksum = "7ff3cbb9817406ac809cd989e47454deee4af89a30b57b436561b676f98af37e"
148
148
 
149
149
  [[package]]
150
150
  name = "portable-atomic"
@@ -3,13 +3,14 @@ members = ["crates/icad-core", "crates/icad-python"]
3
3
  resolver = "3"
4
4
 
5
5
  [workspace.package]
6
- version = "0.3.5"
6
+ # Release version source. Update with: python3 scripts/bump_version.py X.Y.Z
7
+ version = "0.3.8"
7
8
  edition = "2024"
8
9
  rust-version = "1.88"
9
10
  license = "PolyForm-Noncommercial-1.0.0"
10
11
 
11
12
  [workspace.dependencies]
12
- parasolid-core = "=0.3.5"
13
+ parasolid-core = "=0.3.7"
13
14
  pyo3 = { version = "=0.29.2", features = ["abi3-py310"] }
14
15
  flate2 = { version = "1", default-features = false, features = ["rust_backend"] }
15
16
  sha2 = "0.10"
@@ -134,7 +134,7 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
134
134
  DEALINGS IN THE SOFTWARE.
135
135
 
136
136
 
137
- === parasolid-core 0.3.5 (MIT AND Apache-2.0) ===
137
+ === parasolid-core 0.3.7 (MIT AND Apache-2.0) ===
138
138
  MIT License
139
139
 
140
140
  Copyright (c) 2026 parasolid-kit contributors
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: icadkit
3
- Version: 0.3.5
3
+ Version: 0.3.8
4
4
  Classifier: Development Status :: 2 - Pre-Alpha
5
5
  Classifier: Programming Language :: Python :: 3 :: Only
6
6
  Classifier: Programming Language :: Python :: Implementation :: CPython
7
7
  Classifier: Programming Language :: Rust
8
8
  Classifier: Typing :: Typed
9
- Requires-Dist: parasolid-kit[occt]==0.2.0 ; extra == 'preview'
9
+ Requires-Dist: parasolid-kit[occt]==0.3.7 ; extra == 'preview'
10
10
  Provides-Extra: preview
11
11
  License-File: LICENSE
12
12
  License-File: COMMERCIAL-LICENSE.md
@@ -32,27 +32,27 @@ A Rust-based Python reader for **iCAD SX `.icd` files**. Read saved part
32
32
  structure, coordinate frames, names and selected attributes; inspect native
33
33
  box/cylinder parameters; and extract embedded Parasolid geometry.
34
34
 
35
- Version **0.3.5** adds:
36
-
37
- - [View inventories and saved 2D geometry](docs/drawing.md) through
38
- `read_views()` / `read_drawing()` and the `views` / `drawing` CLI commands.
39
- Old originals and registered parts are read directly. Qualified 2D primitives
40
- use view-local coordinates; text content is separate from unsupported layout.
41
- - Catalog-free raw parsing and source B-Rep mapping of
42
- [twenty-six further exact iCAD schema keys](docs/support.md#schemas) through
43
- `parasolid-core 0.3.5`. SPUN_SURF geometry remains partial; saved-body
44
- conversion has separate limits.
45
- - The [trailing container](docs/trailer.md): per-entity blocks with stored
46
- local bounds and the identity of their faces and edges (`read_trailer()`,
47
- `icadkit trailer`).
48
- - [Saved final bodies](docs/saved-bodies.md) bound through saved association
49
- records in V8 and by source ID in V7L2–V7L5, and converted with elliptical
50
- edges, intersection curves and cone apexes.
51
- - More [native primitives](docs/native.md): polygon extrusions, extrusions with
52
- arc profiles, revolved profiles and signed heights, in V7L6/V7L7 and
53
- V8L1/V8L2/V8L3, including internal mirrors.
54
- - [Saved 3D parameter tables](docs/parameters.md) and explicit
55
- [external-reference resolution](docs/references.md).
35
+ Version **0.3.7** added:
36
+
37
+ - [Saved final bodies](docs/saved-bodies.md) on rolling-ball blend surfaces
38
+ and on toroidal faces that reach their axis, through `parasolid-core 0.3.7`
39
+ and the `preview` extra pinned to `parasolid-kit[occt]==0.3.7`. The local
40
+ sample rises from 28,896 to 29,386 of 29,703 bound bodies, with the
41
+ previously converted bodies unchanged.
42
+ - One declaration per backend version (`Cargo.toml`, `pyproject.toml`); the
43
+ runtime checks of the backend version are gone.
44
+
45
+ Version 0.3.6 added [saved final bodies](docs/saved-bodies.md) on spun
46
+ surfaces (SPUN_SURF), line/arc profile extrusions as [CSG](docs/csg.md)
47
+ operands and decoded surface and edge items of the
48
+ [trailing container](docs/trailer.md).
49
+
50
+ Version 0.3.5 added [view inventories and saved 2D geometry](docs/drawing.md),
51
+ twenty-six further catalog-free [schema keys](docs/support.md#schemas), the
52
+ [trailing container](docs/trailer.md), saved final bodies bound through V8
53
+ association records and V7 source IDs, more [native primitives](docs/native.md),
54
+ [saved 3D parameter tables](docs/parameters.md) and explicit
55
+ [external-reference resolution](docs/references.md).
56
56
 
57
57
  Version 0.3.0 added the [native file viewer](docs/viewer.md), broader
58
58
  [part access](docs/parts.md), optional [V7L7 boolean evaluation](docs/csg.md)
@@ -73,7 +73,7 @@ The base reader supports standard GIL-enabled CPython 3.10–3.14. Wheel targets
73
73
  are Linux x86_64 (glibc 2.28+), Windows x86_64 and macOS ARM64 (11+):
74
74
 
75
75
  ```sh
76
- python -m pip install icadkit==0.3.5
76
+ python -m pip install icadkit
77
77
  ```
78
78
 
79
79
  The native extension bundles `parasolid-core` and has no mandatory Python
@@ -82,12 +82,36 @@ explicit compatible catalog is needed only for schemas absent from the built-in
82
82
  profiles. Release files are available on the
83
83
  [GitHub releases page](https://github.com/monozukuri-ai/icadkit/releases).
84
84
 
85
- For preview/GLB, install `python -m pip install 'icadkit[preview]==0.3.5'`.
85
+ For preview/GLB, install `python -m pip install 'icadkit[preview]'`.
86
86
  The optional OCCT dependency requires glibc 2.31+ on Linux; see
87
87
  [preview requirements](docs/preview.md#dependencies-and-validation).
88
88
  To build from a source checkout or sdist, install Rust 1.88+ and a C linker,
89
89
  then run `python -m pip install .` (or `'.[preview]'`).
90
90
 
91
+ ## Updating the package version
92
+
93
+ `[workspace.package].version` in `Cargo.toml` is the single release-version
94
+ source. Both Rust crates inherit it, Maturin derives the Python package version
95
+ from it, and `icadkit.__version__` / the CLI use the installed package metadata.
96
+ With Python 3.11+, Cargo and uv available, update it with one command:
97
+
98
+ ```sh
99
+ python3 scripts/bump_version.py 0.3.8
100
+ python3 scripts/bump_version.py --check --tag v0.3.8
101
+ ```
102
+
103
+ The version above is an example. The command updates the workspace version,
104
+ synchronizes `Cargo.lock` and `uv.lock`, and checks their consistency. On failure
105
+ it restores all three files. Add `--offline` to use cached dependencies. Stable
106
+ `X.Y.Z` and development `X.Y.Z-dev.N` versions are supported; Python metadata
107
+ uses `X.Y.Z.devN` for the latter. `--check` does not modify the source or locks.
108
+ CI checks the same rules and requires release tags to match the source version.
109
+
110
+ The `parasolid-core` and `parasolid-kit` dependency pins have their own versions
111
+ and are updated separately. Historical versions in this README and the
112
+ [changelog](docs/changelog.md) describe when features were introduced; retain
113
+ those numbers and write new release notes when preparing a release.
114
+
91
115
  ## Part structure, frames and attributes
92
116
 
93
117
  ```python
@@ -111,8 +135,9 @@ Qualified V7L7 part frames use millimetres and are evaluated relative to the
111
135
  saved root frame. Qualified standalone primitive owners also expose geometry and saved
112
136
  entity appearance. The separate [CSG API](docs/csg.md) and
113
137
  `uv run --extra preview icadkit view model.icd --csg` evaluate qualified
114
- box/cylinder unions, differences and intersections. Unknown programs retain
115
- diagnostics without substitute geometry.
138
+ unions, differences and intersections of boxes, cylinders, prisms and profile
139
+ extrusions, including saved single-leaf extrusion bodies. Unknown programs
140
+ retain diagnostics without substitute geometry.
116
141
  The [saved final body mode](docs/saved-bodies.md), `view --saved-brep`, also
117
142
  displays qualified V7L7/V8L3 final bodies and imported solids, including bounded
118
143
  spherical, conical and toroidal surfaces. V8L3 resource sharing preserves each
@@ -4,27 +4,27 @@ A Rust-based Python reader for **iCAD SX `.icd` files**. Read saved part
4
4
  structure, coordinate frames, names and selected attributes; inspect native
5
5
  box/cylinder parameters; and extract embedded Parasolid geometry.
6
6
 
7
- Version **0.3.5** adds:
8
-
9
- - [View inventories and saved 2D geometry](docs/drawing.md) through
10
- `read_views()` / `read_drawing()` and the `views` / `drawing` CLI commands.
11
- Old originals and registered parts are read directly. Qualified 2D primitives
12
- use view-local coordinates; text content is separate from unsupported layout.
13
- - Catalog-free raw parsing and source B-Rep mapping of
14
- [twenty-six further exact iCAD schema keys](docs/support.md#schemas) through
15
- `parasolid-core 0.3.5`. SPUN_SURF geometry remains partial; saved-body
16
- conversion has separate limits.
17
- - The [trailing container](docs/trailer.md): per-entity blocks with stored
18
- local bounds and the identity of their faces and edges (`read_trailer()`,
19
- `icadkit trailer`).
20
- - [Saved final bodies](docs/saved-bodies.md) bound through saved association
21
- records in V8 and by source ID in V7L2–V7L5, and converted with elliptical
22
- edges, intersection curves and cone apexes.
23
- - More [native primitives](docs/native.md): polygon extrusions, extrusions with
24
- arc profiles, revolved profiles and signed heights, in V7L6/V7L7 and
25
- V8L1/V8L2/V8L3, including internal mirrors.
26
- - [Saved 3D parameter tables](docs/parameters.md) and explicit
27
- [external-reference resolution](docs/references.md).
7
+ Version **0.3.7** added:
8
+
9
+ - [Saved final bodies](docs/saved-bodies.md) on rolling-ball blend surfaces
10
+ and on toroidal faces that reach their axis, through `parasolid-core 0.3.7`
11
+ and the `preview` extra pinned to `parasolid-kit[occt]==0.3.7`. The local
12
+ sample rises from 28,896 to 29,386 of 29,703 bound bodies, with the
13
+ previously converted bodies unchanged.
14
+ - One declaration per backend version (`Cargo.toml`, `pyproject.toml`); the
15
+ runtime checks of the backend version are gone.
16
+
17
+ Version 0.3.6 added [saved final bodies](docs/saved-bodies.md) on spun
18
+ surfaces (SPUN_SURF), line/arc profile extrusions as [CSG](docs/csg.md)
19
+ operands and decoded surface and edge items of the
20
+ [trailing container](docs/trailer.md).
21
+
22
+ Version 0.3.5 added [view inventories and saved 2D geometry](docs/drawing.md),
23
+ twenty-six further catalog-free [schema keys](docs/support.md#schemas), the
24
+ [trailing container](docs/trailer.md), saved final bodies bound through V8
25
+ association records and V7 source IDs, more [native primitives](docs/native.md),
26
+ [saved 3D parameter tables](docs/parameters.md) and explicit
27
+ [external-reference resolution](docs/references.md).
28
28
 
29
29
  Version 0.3.0 added the [native file viewer](docs/viewer.md), broader
30
30
  [part access](docs/parts.md), optional [V7L7 boolean evaluation](docs/csg.md)
@@ -45,7 +45,7 @@ The base reader supports standard GIL-enabled CPython 3.10–3.14. Wheel targets
45
45
  are Linux x86_64 (glibc 2.28+), Windows x86_64 and macOS ARM64 (11+):
46
46
 
47
47
  ```sh
48
- python -m pip install icadkit==0.3.5
48
+ python -m pip install icadkit
49
49
  ```
50
50
 
51
51
  The native extension bundles `parasolid-core` and has no mandatory Python
@@ -54,12 +54,36 @@ explicit compatible catalog is needed only for schemas absent from the built-in
54
54
  profiles. Release files are available on the
55
55
  [GitHub releases page](https://github.com/monozukuri-ai/icadkit/releases).
56
56
 
57
- For preview/GLB, install `python -m pip install 'icadkit[preview]==0.3.5'`.
57
+ For preview/GLB, install `python -m pip install 'icadkit[preview]'`.
58
58
  The optional OCCT dependency requires glibc 2.31+ on Linux; see
59
59
  [preview requirements](docs/preview.md#dependencies-and-validation).
60
60
  To build from a source checkout or sdist, install Rust 1.88+ and a C linker,
61
61
  then run `python -m pip install .` (or `'.[preview]'`).
62
62
 
63
+ ## Updating the package version
64
+
65
+ `[workspace.package].version` in `Cargo.toml` is the single release-version
66
+ source. Both Rust crates inherit it, Maturin derives the Python package version
67
+ from it, and `icadkit.__version__` / the CLI use the installed package metadata.
68
+ With Python 3.11+, Cargo and uv available, update it with one command:
69
+
70
+ ```sh
71
+ python3 scripts/bump_version.py 0.3.8
72
+ python3 scripts/bump_version.py --check --tag v0.3.8
73
+ ```
74
+
75
+ The version above is an example. The command updates the workspace version,
76
+ synchronizes `Cargo.lock` and `uv.lock`, and checks their consistency. On failure
77
+ it restores all three files. Add `--offline` to use cached dependencies. Stable
78
+ `X.Y.Z` and development `X.Y.Z-dev.N` versions are supported; Python metadata
79
+ uses `X.Y.Z.devN` for the latter. `--check` does not modify the source or locks.
80
+ CI checks the same rules and requires release tags to match the source version.
81
+
82
+ The `parasolid-core` and `parasolid-kit` dependency pins have their own versions
83
+ and are updated separately. Historical versions in this README and the
84
+ [changelog](docs/changelog.md) describe when features were introduced; retain
85
+ those numbers and write new release notes when preparing a release.
86
+
63
87
  ## Part structure, frames and attributes
64
88
 
65
89
  ```python
@@ -83,8 +107,9 @@ Qualified V7L7 part frames use millimetres and are evaluated relative to the
83
107
  saved root frame. Qualified standalone primitive owners also expose geometry and saved
84
108
  entity appearance. The separate [CSG API](docs/csg.md) and
85
109
  `uv run --extra preview icadkit view model.icd --csg` evaluate qualified
86
- box/cylinder unions, differences and intersections. Unknown programs retain
87
- diagnostics without substitute geometry.
110
+ unions, differences and intersections of boxes, cylinders, prisms and profile
111
+ extrusions, including saved single-leaf extrusion bodies. Unknown programs
112
+ retain diagnostics without substitute geometry.
88
113
  The [saved final body mode](docs/saved-bodies.md), `view --saved-brep`, also
89
114
  displays qualified V7L7/V8L3 final bodies and imported solids, including bounded
90
115
  spherical, conical and toroidal surfaces. V8L3 resource sharing preserves each
@@ -4,8 +4,9 @@ icadkit project material is offered under PolyForm Noncommercial 1.0.0, with sep
4
4
  commercial licensing available from UnRobotics Inc.
5
5
  See `LICENSE` and `COMMERCIAL-LICENSE.md`.
6
6
 
7
- Binary distributions also contain `parasolid-core 0.3.5` (MIT AND Apache-2.0)
8
- and PyO3 (MIT OR Apache-2.0, MIT selected). The source metadata declares
7
+ Binary distributions also contain `parasolid-core` (MIT AND Apache-2.0; the
8
+ exact version is listed in `LICENSES/rust-dependencies.txt`) and PyO3 (MIT OR
9
+ Apache-2.0, MIT selected). The source metadata declares
9
10
  `PolyForm-Noncommercial-1.0.0`; the combined Python distribution declares
10
11
  `PolyForm-Noncommercial-1.0.0 AND MIT AND Apache-2.0`. These expressions describe
11
12
  different portions of the package, not a choice to use all icadkit material
@@ -25,9 +26,10 @@ under MIT or Apache-2.0. Third-party components are not relicensed.
25
26
  for project-authored material. Their V30 scalar layouts derive from the
26
27
  MIT/Apache-2.0 `parasolid-core` profile; see `tests/geometry_fixtures.py`
27
28
  for attribution.
28
- - The optional `preview` extra installs `parasolid-kit[occt] 0.2.0` and its
29
- separately distributed OCCT runtime dependencies. These are not bundled in
30
- the icadkit wheel. Their own distributions retain their license terms.
29
+ - The optional `preview` extra installs the `parasolid-kit[occt]` release
30
+ pinned in `pyproject.toml` and its separately distributed OCCT runtime
31
+ dependencies. These are not bundled in the icadkit wheel. Their own
32
+ distributions retain their license terms.
31
33
  Generated viewer directories copy that package's browser assets and retain
32
34
  the included MIT/Zlib notices and the n8ao ISC metadata / CC0-1.0 license-text
33
35
  discrepancy. Keep those notices when redistributing generated viewers.
@@ -1,8 +1,8 @@
1
- {"expected": {"byte_order": "little", "resources": 0}, "family": "synthetic-framing", "holdout": false, "id": "native-uninterpreted", "license": "PolyForm-Noncommercial-1.0.0; see corpus/README.md for profile notices", "path": "native-uninterpreted.icd", "provenance": "authored synthetic; corpus/generate.py and tests/geometry_fixtures.py", "sha256": "d4bb2e63c5cccdac66488f8679740b5962713b9d7dbee0e252f584b722958fc7"}
2
- {"expected": {"byte_order": "little", "codes": [], "resources": 1, "status": {"brep": "complete", "extraction": "complete", "raw_geometry": "complete", "topology": "complete"}, "wire": {"counts": {"bodies": 1, "curves": 1, "edges": 1, "faces": 0, "points": 2, "vertices": 2}, "length": 5}}, "family": "synthetic-wire", "holdout": false, "id": "wire-little-raw", "license": "PolyForm-Noncommercial-1.0.0; see corpus/README.md for profile notices", "path": "wire-little-raw.icd", "provenance": "authored synthetic; corpus/generate.py and tests/geometry_fixtures.py", "sha256": "75102af13dca3d14d69f268b16fc2889242cb23e64365f36b7459c570e9a85bf"}
3
- {"expected": {"byte_order": "little", "codes": [], "resources": 1, "status": {"brep": "complete", "extraction": "complete", "raw_geometry": "complete", "topology": "complete"}, "wire": {"counts": {"bodies": 1, "curves": 1, "edges": 1, "faces": 0, "points": 2, "vertices": 2}, "length": 5}}, "family": "synthetic-wire", "holdout": false, "id": "wire-little-zlib", "license": "PolyForm-Noncommercial-1.0.0; see corpus/README.md for profile notices", "path": "wire-little-zlib.icd", "provenance": "authored synthetic; corpus/generate.py and tests/geometry_fixtures.py", "sha256": "a2ef56b2ba29e11ecfd838f41e6d87c7cc6587123b4ef7832a45b3bb10a53d2b"}
4
- {"expected": {"byte_order": "big", "codes": [], "resources": 1, "status": {"brep": "complete", "extraction": "complete", "raw_geometry": "complete", "topology": "complete"}, "wire": {"counts": {"bodies": 1, "curves": 1, "edges": 1, "faces": 0, "points": 2, "vertices": 2}, "length": 5}}, "family": "synthetic-wire", "holdout": false, "id": "wire-big-raw", "license": "PolyForm-Noncommercial-1.0.0; see corpus/README.md for profile notices", "path": "wire-big-raw.icd", "provenance": "authored synthetic; corpus/generate.py and tests/geometry_fixtures.py", "sha256": "68ed994d8917e553b59381eb1d56874c4fc7243080fd0b45cee027066bf7829f"}
5
- {"expected": {"byte_order": "big", "codes": [], "resources": 1, "status": {"brep": "complete", "extraction": "complete", "raw_geometry": "complete", "topology": "complete"}, "wire": {"counts": {"bodies": 1, "curves": 1, "edges": 1, "faces": 0, "points": 2, "vertices": 2}, "length": 5}}, "family": "synthetic-wire", "holdout": false, "id": "wire-big-zlib", "license": "PolyForm-Noncommercial-1.0.0; see corpus/README.md for profile notices", "path": "wire-big-zlib.icd", "provenance": "authored synthetic; corpus/generate.py and tests/geometry_fixtures.py", "sha256": "c18e05dea1a40493193a7662557663f5b17b8d6204bbe1ce03b3c43ceceaecc9"}
6
- {"expected": {"byte_order": "little", "codes": ["schema.missing_base_schema"], "resources": 1, "status": {"brep": "not_checked", "extraction": "complete", "raw_geometry": "unsupported", "topology": "not_checked"}}, "family": "synthetic-wire", "holdout": false, "id": "unknown-schema", "license": "PolyForm-Noncommercial-1.0.0; see corpus/README.md for profile notices", "path": "unknown-schema.icd", "provenance": "authored synthetic; corpus/generate.py and tests/geometry_fixtures.py", "sha256": "18df3e23fa400bc197b9f13c99b280682d92d05bcbbe9bfa0d12fd185996977d"}
7
- {"expected": {"byte_order": "little", "codes": ["brep.invalid_reference"], "resources": 1, "status": {"brep": "invalid", "extraction": "complete", "raw_geometry": "complete", "topology": "not_checked"}}, "family": "synthetic-wire", "holdout": false, "id": "invalid-reference", "license": "PolyForm-Noncommercial-1.0.0; see corpus/README.md for profile notices", "path": "invalid-reference.icd", "provenance": "authored synthetic; corpus/generate.py and tests/geometry_fixtures.py", "sha256": "c684eb6d0935eb7693386e8221d08a73378414a2c80ce24deeb8929a533f962d"}
8
- {"expected": {"byte_order": "little", "codes": ["compression.invalid"], "resources": 1, "status": {"brep": "not_checked", "extraction": "invalid", "raw_geometry": "not_checked", "topology": "not_checked"}}, "family": "synthetic-wire", "holdout": false, "id": "invalid-checksum", "license": "PolyForm-Noncommercial-1.0.0; see corpus/README.md for profile notices", "path": "invalid-checksum.icd", "provenance": "authored synthetic; corpus/generate.py and tests/geometry_fixtures.py", "sha256": "cc4e1cdf103193a503db080b0e694865bfaff133ef198c7c41460f093c90e426"}
1
+ {"expected": {"byte_order": "little", "resources": 0}, "family": "synthetic-framing", "holdout": false, "id": "native-uninterpreted", "license": "PolyForm-Noncommercial-1.0.0; see corpus/README.md for profile notices", "path": "native-uninterpreted.icd", "provenance": "authored synthetic; corpus/generate.py and tests/geometry_fixtures.py", "sha256": "2623d141699e0a846ac1d745b8eb4761421cd6bea4cef7ffb79545919e66f62a"}
2
+ {"expected": {"byte_order": "little", "codes": [], "resources": 1, "status": {"brep": "complete", "extraction": "complete", "raw_geometry": "complete", "topology": "complete"}, "wire": {"counts": {"bodies": 1, "curves": 1, "edges": 1, "faces": 0, "points": 2, "vertices": 2}, "length": 5}}, "family": "synthetic-wire", "holdout": false, "id": "wire-little-raw", "license": "PolyForm-Noncommercial-1.0.0; see corpus/README.md for profile notices", "path": "wire-little-raw.icd", "provenance": "authored synthetic; corpus/generate.py and tests/geometry_fixtures.py", "sha256": "90049eb96aa3b89c55a9c912efdfcccf41dbf491ccf6f66af84e9acc5185404d"}
3
+ {"expected": {"byte_order": "little", "codes": [], "resources": 1, "status": {"brep": "complete", "extraction": "complete", "raw_geometry": "complete", "topology": "complete"}, "wire": {"counts": {"bodies": 1, "curves": 1, "edges": 1, "faces": 0, "points": 2, "vertices": 2}, "length": 5}}, "family": "synthetic-wire", "holdout": false, "id": "wire-little-zlib", "license": "PolyForm-Noncommercial-1.0.0; see corpus/README.md for profile notices", "path": "wire-little-zlib.icd", "provenance": "authored synthetic; corpus/generate.py and tests/geometry_fixtures.py", "sha256": "a768f5d63db6bb91f3178c52304002b3dde9eaef7fdd0c0bd5c61160cd94d0ec"}
4
+ {"expected": {"byte_order": "big", "codes": [], "resources": 1, "status": {"brep": "complete", "extraction": "complete", "raw_geometry": "complete", "topology": "complete"}, "wire": {"counts": {"bodies": 1, "curves": 1, "edges": 1, "faces": 0, "points": 2, "vertices": 2}, "length": 5}}, "family": "synthetic-wire", "holdout": false, "id": "wire-big-raw", "license": "PolyForm-Noncommercial-1.0.0; see corpus/README.md for profile notices", "path": "wire-big-raw.icd", "provenance": "authored synthetic; corpus/generate.py and tests/geometry_fixtures.py", "sha256": "222597e831d61fb1b57f78251155c9cda08419056e258333570f514b58327e33"}
5
+ {"expected": {"byte_order": "big", "codes": [], "resources": 1, "status": {"brep": "complete", "extraction": "complete", "raw_geometry": "complete", "topology": "complete"}, "wire": {"counts": {"bodies": 1, "curves": 1, "edges": 1, "faces": 0, "points": 2, "vertices": 2}, "length": 5}}, "family": "synthetic-wire", "holdout": false, "id": "wire-big-zlib", "license": "PolyForm-Noncommercial-1.0.0; see corpus/README.md for profile notices", "path": "wire-big-zlib.icd", "provenance": "authored synthetic; corpus/generate.py and tests/geometry_fixtures.py", "sha256": "9e0aaf42f3d23941c1ecadb354ef026abc4bf9b40979b76a36f71ac996825d93"}
6
+ {"expected": {"byte_order": "little", "codes": ["schema.missing_base_schema"], "resources": 1, "status": {"brep": "not_checked", "extraction": "complete", "raw_geometry": "unsupported", "topology": "not_checked"}}, "family": "synthetic-wire", "holdout": false, "id": "unknown-schema", "license": "PolyForm-Noncommercial-1.0.0; see corpus/README.md for profile notices", "path": "unknown-schema.icd", "provenance": "authored synthetic; corpus/generate.py and tests/geometry_fixtures.py", "sha256": "6e6eb075c99ac512366cb0f2dc049cbd5baf52acafd5916f11fbe349ef74793c"}
7
+ {"expected": {"byte_order": "little", "codes": ["brep.invalid_reference"], "resources": 1, "status": {"brep": "invalid", "extraction": "complete", "raw_geometry": "complete", "topology": "not_checked"}}, "family": "synthetic-wire", "holdout": false, "id": "invalid-reference", "license": "PolyForm-Noncommercial-1.0.0; see corpus/README.md for profile notices", "path": "invalid-reference.icd", "provenance": "authored synthetic; corpus/generate.py and tests/geometry_fixtures.py", "sha256": "1f796bff73c5c3a536832edaca09718bd3ecedd5645535809806b4e7aaa03f02"}
8
+ {"expected": {"byte_order": "little", "codes": ["compression.invalid"], "resources": 1, "status": {"brep": "not_checked", "extraction": "invalid", "raw_geometry": "not_checked", "topology": "not_checked"}}, "family": "synthetic-wire", "holdout": false, "id": "invalid-checksum", "license": "PolyForm-Noncommercial-1.0.0; see corpus/README.md for profile notices", "path": "invalid-checksum.icd", "provenance": "authored synthetic; corpus/generate.py and tests/geometry_fixtures.py", "sha256": "c4b6299ddd93d618fae769272df983e4f0a20b5d915c853792d7842e0720a0de"}
@@ -1,4 +1,4 @@
1
- Generated by scripts/check_license.py --write-crate-notices.
1
+ Generated by scripts/check_license.py --write.
2
2
  Canonical source notices follow; paths refer to the source root.
3
3
 
4
4
  ===== LICENSE =====
@@ -108,8 +108,9 @@ icadkit project material is offered under PolyForm Noncommercial 1.0.0, with sep
108
108
  commercial licensing available from UnRobotics Inc.
109
109
  See `LICENSE` and `COMMERCIAL-LICENSE.md`.
110
110
 
111
- Binary distributions also contain `parasolid-core 0.3.5` (MIT AND Apache-2.0)
112
- and PyO3 (MIT OR Apache-2.0, MIT selected). The source metadata declares
111
+ Binary distributions also contain `parasolid-core` (MIT AND Apache-2.0; the
112
+ exact version is listed in `LICENSES/rust-dependencies.txt`) and PyO3 (MIT OR
113
+ Apache-2.0, MIT selected). The source metadata declares
113
114
  `PolyForm-Noncommercial-1.0.0`; the combined Python distribution declares
114
115
  `PolyForm-Noncommercial-1.0.0 AND MIT AND Apache-2.0`. These expressions describe
115
116
  different portions of the package, not a choice to use all icadkit material
@@ -129,9 +130,10 @@ under MIT or Apache-2.0. Third-party components are not relicensed.
129
130
  for project-authored material. Their V30 scalar layouts derive from the
130
131
  MIT/Apache-2.0 `parasolid-core` profile; see `tests/geometry_fixtures.py`
131
132
  for attribution.
132
- - The optional `preview` extra installs `parasolid-kit[occt] 0.2.0` and its
133
- separately distributed OCCT runtime dependencies. These are not bundled in
134
- the icadkit wheel. Their own distributions retain their license terms.
133
+ - The optional `preview` extra installs the `parasolid-kit[occt]` release
134
+ pinned in `pyproject.toml` and its separately distributed OCCT runtime
135
+ dependencies. These are not bundled in the icadkit wheel. Their own
136
+ distributions retain their license terms.
135
137
  Generated viewer directories copy that package's browser assets and retain
136
138
  the included MIT/Zlib notices and the n8ao ISC metadata / CC0-1.0 license-text
137
139
  discrepancy. Keep those notices when redistributing generated viewers.
@@ -582,7 +584,7 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
582
584
  DEALINGS IN THE SOFTWARE.
583
585
 
584
586
 
585
- === parasolid-core 0.3.5 (MIT AND Apache-2.0) ===
587
+ === parasolid-core 0.3.7 (MIT AND Apache-2.0) ===
586
588
  MIT License
587
589
 
588
590
  Copyright (c) 2026 parasolid-kit contributors
@@ -0,0 +1,49 @@
1
+ //! Records the resolved `parasolid-core` version from the workspace lock file.
2
+ //!
3
+ //! The crate reports which backend release it was compiled with
4
+ //! (`PARASOLID_CORE_VERSION`). Reading it from `Cargo.lock` keeps the manifest
5
+ //! pin the only place in the source that names that version.
6
+
7
+ use std::env;
8
+ use std::fs;
9
+ use std::path::{Path, PathBuf};
10
+
11
+ fn main() -> Result<(), String> {
12
+ let manifest_dir = env::var_os("CARGO_MANIFEST_DIR").ok_or("CARGO_MANIFEST_DIR is not set")?;
13
+ let lock = PathBuf::from(manifest_dir)
14
+ .ancestors()
15
+ .map(|dir| dir.join("Cargo.lock"))
16
+ .find(|path| path.is_file())
17
+ .ok_or("no Cargo.lock above the crate directory")?;
18
+ let version = resolved_version(&lock, "parasolid-core")?;
19
+ println!("cargo:rerun-if-changed={}", lock.display());
20
+ println!("cargo:rustc-env=ICAD_PARASOLID_CORE_VERSION={version}");
21
+ Ok(())
22
+ }
23
+
24
+ /// The version of `package` in the lock file, which must resolve it exactly once.
25
+ fn resolved_version(lock: &Path, package: &str) -> Result<String, String> {
26
+ let text = fs::read_to_string(lock)
27
+ .map_err(|error| format!("cannot read {}: {error}", lock.display()))?;
28
+ let mut versions = Vec::new();
29
+ let mut name = None;
30
+ for line in text.lines().map(str::trim) {
31
+ if line == "[[package]]" {
32
+ name = None;
33
+ } else if let Some(value) = line.strip_prefix("name = ") {
34
+ name = Some(value.trim_matches('"'));
35
+ } else if let Some(value) = line.strip_prefix("version = ")
36
+ && name == Some(package)
37
+ {
38
+ versions.push(value.trim_matches('"').to_owned());
39
+ }
40
+ }
41
+ match versions.as_slice() {
42
+ [version] => Ok(version.clone()),
43
+ [] => Err(format!("{package} is not resolved in {}", lock.display())),
44
+ _ => Err(format!(
45
+ "{package} resolves to several versions in {}",
46
+ lock.display()
47
+ )),
48
+ }
49
+ }