Dimensions: a file whose dimensions carry options this version doesn't know still opens; commands still refuse unknown options

This commit is contained in:
bflatastic
2026-10-16 01:36:24 +07:00
parent 8aea665c4e
commit 7217dcd121
16 changed files with 63 additions and 10 deletions
+17 -5
View File
@@ -123,7 +123,7 @@ pub enum TextAlignment {
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(default, rename_all = "camelCase", deny_unknown_fields)]
#[serde(default, rename_all = "camelCase")]
pub struct DimensionOptions {
pub units: LengthUnit,
pub precision: usize,
@@ -182,6 +182,18 @@ impl Default for DimensionOptions {
}
impl DimensionOptions {
/// Options given to a command or a tool: unknown keys are refused there, while files load
/// tolerantly so a newer version's options don't stop a document opening.
pub fn from_params(v: serde_json::Value) -> Result<Self, String> {
if let (Some(given), Ok(serde_json::Value::Object(known))) = (v.as_object(), serde_json::to_value(Self::default()))
&& let Some(k) = given.keys().find(|k| !known.contains_key(*k))
{
return Err(format!("Unknown dimension option {k}"));
}
let o: Self = serde_json::from_value(v).map_err(|e| e.to_string())?;
o.validate()?;
Ok(o)
}
pub fn factor(&self) -> Result<f64, String> {
let (a, b) = self.scale.split_once(':').ok_or("Scale must be drawing:real, for example 1:10")?;
let parse = |s: &str| s.trim().parse::<f64>().ok().filter(|n| n.is_finite() && *n > 0.0 && *n <= 1e9);
@@ -236,7 +248,7 @@ impl DimensionOptions {
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[serde(rename_all = "camelCase")]
pub struct SegmentRef {
pub node: NodeId,
pub subpath: usize,
@@ -263,7 +275,7 @@ impl SegmentRef {
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "camelCase", deny_unknown_fields)]
#[serde(tag = "kind", rename_all = "camelCase")]
pub enum DimensionSource {
Segment {
edge: SegmentRef,
@@ -312,7 +324,7 @@ impl DimensionSource {
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[serde(rename_all = "camelCase")]
pub struct PointRef {
pub node: NodeId,
pub subpath: usize,
@@ -326,7 +338,7 @@ impl PointRef {
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[serde(rename_all = "camelCase")]
pub struct Dimension {
pub mode: DimensionMode,
pub a: Point,
+1 -3
View File
@@ -46,9 +46,7 @@ fn error(s: impl ToString) -> EngineError {
fn options(s: &Session, p: &Value) -> Result<DimensionOptions> {
let default = s.tool_options_of("dimension");
let value = p.get("options").cloned().unwrap_or_else(|| default.get("style").cloned().unwrap_or(json!({})));
let o: DimensionOptions = serde_json::from_value(value).map_err(error)?;
o.validate().map_err(error)?;
Ok(o)
DimensionOptions::from_params(value).map_err(error)
}
fn point(p: &Value, key: &str) -> Result<Point> {
+1 -2
View File
@@ -455,8 +455,7 @@ impl Session {
}
if tool.unwrap_or(self.tool_id()) == "dimension" {
if let Some(style) = values.get("style") {
let options: vectorcraft_doc::dimension::DimensionOptions = serde_json::from_value(style.clone()).map_err(|e| e.to_string())?;
options.validate()?;
vectorcraft_doc::dimension::DimensionOptions::from_params(style.clone())?;
}
if let Some(mode) = values.get("mode")
&& !mode.as_str().is_some_and(|m| ["linear", "angular", "radial", "radius", "diameter"].contains(&m))
+32
View File
@@ -357,3 +357,35 @@ fn dimension_cannot_measure_its_own_generated_art() {
let child = s.doc().unwrap().doc.node(NodeId(id)).unwrap().children().unwrap()[0].id;
assert!(s.execute("dimension.create", &json!({"source":{"kind":"segment","edge":{"node":child,"subpath":0,"segment":0}}})).is_err());
}
#[test]
fn unknown_dimension_options_are_refused_by_commands_but_a_newer_file_still_opens() {
let mut s = session();
let line = edge(&mut s);
let source = json!({"kind":"segment","edge":{"node":line,"subpath":0,"segment":0}});
let bad = s.execute("dimension.create", &json!({"source":source,"options":{"units":"in","color":"red"}}));
assert!(bad.unwrap_err().to_string().contains("Unknown dimension option color"));
assert!(s.set_tool_option_cmd(&json!({"tool":"dimension","values":{"style":{"precison":2}}})).is_err());
let id = dim(&mut s, line);
let saved = vectorcraft_format::save(&s.doc().unwrap().doc, false);
let mut v: Value = serde_json::from_slice(&saved).unwrap();
// A later version's dimension carries fields this one doesn't know.
fn add_unknown(v: &mut Value) {
match v {
Value::Object(m) => {
if let Some(Value::Object(d)) = m.get_mut("dimension") {
d.insert("later".into(), json!(1));
if let Some(Value::Object(o)) = d.get_mut("options") {
o.insert("later".into(), json!(true));
}
}
m.values_mut().for_each(add_unknown);
}
Value::Array(a) => a.iter_mut().for_each(add_unknown),
_ => {}
}
}
add_unknown(&mut v);
let doc = vectorcraft_format::load(&serde_json::to_vec(&v).unwrap()).unwrap();
assert!(doc.node(NodeId(id)).unwrap().extras().dimension.is_some());
}
+1
View File
@@ -4851,6 +4851,7 @@ mark-style Japanese Japonské
@msg Scale ratio must be between 1:1000000 and 1000000:1 Poměr měřítka musí být mezi 1:1000000 a 1000000:1
@msg Second measured segment no longer exists Druhý měřený segment už neexistuje
@msg Unknown dimension mode Neznámý režim kóty
@msg Unknown dimension option {k} Neznámá volba kóty {k}
@msg Unknown dimension style Neznámý styl kóty
@msg Unknown unit Neznámá jednotka
# Revolve cap description
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4380,6 +4380,7 @@ theme Appearance Erscheinungsbild
@msg Scale ratio must be between 1:1000000 and 1000000:1 Das Maßstabsverhältnis muss zwischen 1:1000000 und 1000000:1 liegen
@msg Second measured segment no longer exists Das zweite gemessene Segment existiert nicht mehr
@msg Unknown dimension mode Unbekannter Bemaßungsmodus
@msg Unknown dimension option {k} Unbekannte Bemaßungsoption {k}
@msg Unknown dimension style Unbekannter Bemaßungsstil
@msg Unknown unit Unbekannte Einheit
# Revolve cap description
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4792,6 +4792,7 @@ mark-style Japanese Japonesas
@msg Scale ratio must be between 1:1000000 and 1000000:1 La relación de escala debe estar entre 1:1000000 y 1000000:1
@msg Second measured segment no longer exists El segundo segmento medido ya no existe
@msg Unknown dimension mode Modo de cota desconocido
@msg Unknown dimension option {k} Opción de cota desconocida {k}
@msg Unknown dimension style Estilo de cota desconocido
@msg Unknown unit Unidad desconocida
# Revolve cap description
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4850,6 +4850,7 @@ theme Appearance Apparence
@msg Scale ratio must be between 1:1000000 and 1000000:1 Le rapport d’échelle doit être compris entre 1:1000000 et 1000000:1
@msg Second measured segment no longer exists Le second segment mesuré n’existe plus
@msg Unknown dimension mode Mode de cote inconnu
@msg Unknown dimension option {k} Option de cote inconnue {k}
@msg Unknown dimension style Style de cote inconnu
@msg Unknown unit Unité inconnue
# Revolve cap description
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4817,6 +4817,7 @@ imageTrace Outline Körvonal
@msg Scale ratio must be between 1:1000000 and 1000000:1 A lépték aránya 1:1000000 és 1000000:1 között lehet
@msg Second measured segment no longer exists A második mért szakasz már nem létezik
@msg Unknown dimension mode Ismeretlen méretezési mód
@msg Unknown dimension option {k} Ismeretlen méretezési beállítás: {k}
@msg Unknown dimension style Ismeretlen méretezési stílus
@msg Unknown unit Ismeretlen mértékegység
# Revolve cap description
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4425,6 +4425,7 @@ lighting Light Luce
@msg Scale ratio must be between 1:1000000 and 1000000:1 Il rapporto di scala deve essere compreso tra 1:1000000 e 1000000:1
@msg Second measured segment no longer exists Il secondo segmento misurato non esiste più
@msg Unknown dimension mode Modalità di quota sconosciuta
@msg Unknown dimension option {k} Opzione di quota sconosciuta {k}
@msg Unknown dimension style Stile di quota sconosciuto
@msg Unknown unit Unità sconosciuta
# Revolve cap description
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4420,6 +4420,7 @@ lighting Light ライト
@msg Scale ratio must be between 1:1000000 and 1000000:1 縮尺の比率は 1:1000000 から 1000000:1 の間にしてください
@msg Second measured segment no longer exists 2 番目の測定セグメントはもう存在しません
@msg Unknown dimension mode 寸法のモードが不明です
@msg Unknown dimension option {k} 不明な寸法オプション {k}
@msg Unknown dimension style 寸法のスタイルが不明です
@msg Unknown unit 単位が不明です
# Revolve cap description
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4389,6 +4389,7 @@ lighting Light 광원
@msg Scale ratio must be between 1:1000000 and 1000000:1 축척 비율은 1:1000000에서 1000000:1 사이여야 합니다
@msg Second measured segment no longer exists 두 번째 측정 세그먼트가 더 이상 없습니다
@msg Unknown dimension mode 알 수 없는 치수 모드
@msg Unknown dimension option {k} 알 수 없는 치수 옵션 {k}
@msg Unknown dimension style 알 수 없는 치수 스타일
@msg Unknown unit 알 수 없는 단위
# Revolve cap description
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4692,6 +4692,7 @@ mark-style Japanese Japońskie
@msg Scale ratio must be between 1:1000000 and 1000000:1 Współczynnik skali musi mieścić się między 1:1000000 a 1000000:1
@msg Second measured segment no longer exists Drugi mierzony odcinek już nie istnieje
@msg Unknown dimension mode Nieznany tryb wymiaru
@msg Unknown dimension option {k} Nieznana opcja wymiaru {k}
@msg Unknown dimension style Nieznany styl wymiaru
@msg Unknown unit Nieznana jednostka
# Revolve cap description
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4421,6 +4421,7 @@ lighting Light Свет
@msg Scale ratio must be between 1:1000000 and 1000000:1 Соотношение масштаба должно быть от 1:1000000 до 1000000:1
@msg Second measured segment no longer exists Второй измеряемый сегмент больше не существует
@msg Unknown dimension mode Неизвестный режим размера
@msg Unknown dimension option {k} Неизвестный параметр размера {k}
@msg Unknown dimension style Неизвестный стиль размера
@msg Unknown unit Неизвестная единица
# Revolve cap description
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4405,6 +4405,7 @@ lighting Light Світло
@msg Scale ratio must be between 1:1000000 and 1000000:1 Співвідношення масштабу має бути від 1:1000000 до 1000000:1
@msg Second measured segment no longer exists Другий виміряний сегмент більше не існує
@msg Unknown dimension mode Невідомий режим розміру
@msg Unknown dimension option {k} Невідомий параметр розміру {k}
@msg Unknown dimension style Невідомий стиль розміру
@msg Unknown unit Невідома одиниця
# Revolve cap description
Can't render this file because it contains an unexpected character in line 713 and column 40.
+1
View File
@@ -4404,6 +4404,7 @@ theme Appearance Giao diện
@msg Scale ratio must be between 1:1000000 and 1000000:1 Tỷ lệ phải nằm trong khoảng 1:1000000 đến 1000000:1
@msg Second measured segment no longer exists Đoạn được đo thứ hai không còn tồn tại
@msg Unknown dimension mode Chế độ kích thước không xác định
@msg Unknown dimension option {k} Tùy chọn kích thước không xác định {k}
@msg Unknown dimension style Kiểu kích thước không xác định
@msg Unknown unit Đơn vị không xác định
# Revolve cap description
Can't render this file because it contains an unexpected character in line 3682 and column 10.