Merge pull request #2007 from JonRC/fix/stroke-cap-error-values

fix(engine): an unknown stroke cap lists the caps node.set takes (#2005)
This commit is contained in:
BFlat authored and GitHub committed 2026-11-07 09:42:05 +08:00
commit 6bbaa27f97
3 files changed
+35 -3

No files matched your search

+6 -3
View File
@@ -220,6 +220,9 @@ fn set_operand_op(doc: &mut Document, id: NodeId, v: &Value, cmd: &str) -> Resul
pub(crate) const WIDTH_PROFILE_HELP: &str = "widthProfile: a preset (uniform|wedge|taper|eye|rounded|pinch), points [[t (0-1 along the path), scale (× stroke weight)] | [t, left, right] (one-sided: each side's scale, looking along the path), ...] or [{t, scale} | {t, left, right}], {preset} | {points}, or null (uniform)";
/// The stroke caps `strokeCap`, `startCap` and `endCap` take (every [`StrokeCap`]).
pub(crate) const STROKE_CAP_HELP: &str = "none|round|square|lineArrow|triangleArrow|reversedTriangle|circleFilled|diamondFilled";
pub(crate) const BRUSH_HELP: &str = "brush: a preset (taper|ink|ribbon|bristle: stretched; dots|stars|confetti|leaves: scattered), {preset, spacing? (× weight), sizeJitter? (0-1), rotationJitter? (0-1), scatter? (0-4 × weight), seed?}, or null (a plain stroke)";
/// A brush from a preset id, `{preset, spacing?, sizeJitter?, rotationJitter?, scatter?, seed?}`
@@ -635,13 +638,13 @@ fn apply_look(cmd: &str, doc: &mut Document, id: NodeId, k: &str, v: &Value) ->
st.sides = Some(sides);
}
"strokeCap" => {
let c: StrokeCap = enum_of(v).ok_or_else(|| bad(cmd, "unknown stroke cap"))?;
let c: StrokeCap = enum_of(v).ok_or_else(|| bad(cmd, format!("strokeCap: {STROKE_CAP_HELP}")))?;
let n = doc.get_mut(id)?;
n.stroke.cap_start = c;
n.stroke.cap_end = c;
}
"startCap" => doc.get_mut(id)?.stroke.cap_start = enum_of(v).ok_or_else(|| bad(cmd, "unknown stroke cap"))?,
"endCap" => doc.get_mut(id)?.stroke.cap_end = enum_of(v).ok_or_else(|| bad(cmd, "unknown stroke cap"))?,
"startCap" => doc.get_mut(id)?.stroke.cap_start = enum_of(v).ok_or_else(|| bad(cmd, format!("startCap: {STROKE_CAP_HELP}")))?,
"endCap" => doc.get_mut(id)?.stroke.cap_end = enum_of(v).ok_or_else(|| bad(cmd, format!("endCap: {STROKE_CAP_HELP}")))?,
"strokeJoin" => doc.get_mut(id)?.stroke.join = enum_of(v).ok_or_else(|| bad(cmd, "strokeJoin: miter|bevel|round"))?,
"miterLimit" => doc.get_mut(id)?.stroke.miter_limit = f64_of(v).unwrap_or(4.0).clamp(1.0, 100.0),
"miterAngle" => {
+2
View File
@@ -545,6 +545,8 @@ mod slot_tests;
#[cfg(test)]
mod stored_ids_tests;
#[cfg(test)]
mod stroke_cap_error_tests;
#[cfg(test)]
mod stroke_style_tests;
#[cfg(test)]
mod style_tests;
@@ -0,0 +1,27 @@
//! An unknown stroke cap is refused with the caps there are, as `strokeAlign` and `strokeJoin`
//! list theirs (#2005).
use serde_json::json;
use crate::Session;
use crate::cmd::props::STROKE_CAP_HELP;
use crate::params::id_of;
use figcraft_doc::StrokeCap;
#[test]
fn an_unknown_cap_lists_every_cap() {
let names: Vec<String> = StrokeCap::ALL.iter().map(|c| serde_json::to_value(c).unwrap().as_str().unwrap().to_string()).collect();
assert_eq!(STROKE_CAP_HELP.split('|').collect::<Vec<_>>(), names, "the list is every cap, in order");
let mut s = Session::new();
let v = id_of(&s.execute("vector.create", &json!({"path": "M0 0 L200 0", "x": 0, "y": 0})).unwrap()["id"]).unwrap();
for key in ["strokeCap", "startCap", "endCap"] {
for junk in [json!("butt"), json!(3), json!("")] {
let e = s.execute("node.set", &json!({"id": v, "props": {key: junk}})).unwrap_err().to_string();
assert!(e.contains(&format!("{key}: {STROKE_CAP_HELP}")), "{key} {junk}: {e}");
}
// Every name the error lists is taken.
for name in &names {
s.execute("node.set", &json!({"id": v, "props": {key: name}})).unwrap_or_else(|e| panic!("{key} {name}: {e}"));
}
}
}