Design properties: project, author and licence in the design, its exports and inspect_design (#1015) (#1189)

A design carries metadata (format 21): project, part number, revision, author, organization, description and license, plus other tools' data under reverse-DNS keys, kept as written. Edited in the Design Properties window (Document Settings), set by document.metadata.set, reported by inspect_design, and written into STEP, IGES and 3MF where the format has a place for them. Translated, with tests.
This commit is contained in:
Lasse Lian authored and GitHub committed 2026-10-14 18:25:35 -04:00
1 parent 78079587ad
commit a4a8b48567
33 files changed
+1106 -21

No files matched your search

+17 -1
View File
@@ -158,6 +158,12 @@
//! transformation (an absent field means no setups, and empty render state is not saved); the
//! version stamp makes 0.11.0 (which reads up to 20) ask for an update instead of silently
//! dropping saved setups.
//! Also first saved in 21, a design can carry `metadata` (#1015): design properties
//! (`project`, `part_number`, `revision`, `author`, `organization`, `description`, `license`,
//! each text) and other tools' data, each tool's under its own reverse-DNS key
//! (`com.example.tool`). Every key is kept as it is, and nothing in it is checked when the
//! design opens. A design without it saves as before. 0.11.x refuses format 21, so no
//! released build drops the properties when it saves.
use serde_json::Value;
@@ -400,7 +406,7 @@ pub fn read(s: &str) -> Result<Document> {
"this design was saved by a newer SolidCraft (file format {version}; this version reads formats up to {FORMAT}): update SolidCraft to open it"
)));
}
check_numbers(&v, 0)?;
check_design_numbers(&v)?;
crate::config::validate_rules_json(&v)?;
crate::config::validate_styles_json(&v)?;
crate::face_anchor::validate_face_anchors_json(&v)?;
@@ -869,6 +875,16 @@ fn validate_freeform_offsets(value: &Value) -> Result<()> {
/// outside what the kernel's tolerances serve.
const MAX_REAL: f64 = 1e9;
/// Every number the design itself holds is in range ([`check_numbers`]). `metadata` is left
/// alone: it is other tools' data, kept as it is and never read as a number (#1015); parsing
/// already bounds its nesting.
fn check_design_numbers(v: &Value) -> Result<()> {
match v {
Value::Object(m) => m.iter().filter(|(k, _)| k.as_str() != "metadata").try_for_each(|(_, x)| check_numbers(x, 1)),
v => check_numbers(v, 0),
}
}
/// Every non-integer number in the file is finite and within ±`MAX_REAL` (a damaged or hostile
/// file is refused instead of overflowing the geometry later).
fn check_numbers(v: &Value, depth: usize) -> Result<()> {