Engine: a parameter error carries its key and problem (EngineError::Param)

A form can show the problem beside the field it is about. The message
still names the key, as before, for agents.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
jyvikoandClaude Opus 5.5 authored and Irakli Kourtis committed 2026-10-14 20:58:45 -04:00
1 parent 8df4ea040c
commit 9dcbb9c9a6
14 files changed
+60 -49

No files matched your search

+7 -9
View File
@@ -53,11 +53,9 @@ fn analytic(s: FaceSurface) -> Surface {
pub(super) fn number(p: &Value, key: &str) -> Result<Option<f64>> {
match p.get(key) {
None => Ok(None),
Some(v) => v
.as_f64()
.filter(|x| x.is_finite() && *x > 0.0)
.map(Some)
.ok_or_else(|| EngineError::Params(format!("`{key}` must be a positive number (mm)"))),
Some(v) => {
v.as_f64().filter(|x| x.is_finite() && *x > 0.0).map(Some).ok_or_else(|| EngineError::param(key, "must be a positive number (mm)"))
}
}
}
@@ -80,7 +78,7 @@ pub(super) fn inputs<'a>(s: &'a Session, p: &Value) -> Result<Vec<Input<'a>>> {
let tolerance = number(p, "tolerance")?;
match (p.get("body"), p.get("surface")) {
(Some(b), None) => {
let name = b.as_str().ok_or_else(|| EngineError::Params("`body` must be a body name".into()))?;
let name = b.as_str().ok_or_else(|| EngineError::param("body", "must be a body name"))?;
let shape = s
.design
.as_deref()
@@ -153,14 +151,14 @@ fn hex_blocks(s: &mut Session, p: &Value) -> Result<Value> {
let shapes: Vec<&Shape> = match p.get("bodies") {
None => design.bodies.iter().collect(),
Some(v) => {
let names = v.as_array().ok_or_else(|| EngineError::Params("`bodies` must be a list of body names".into()))?;
let names = v.as_array().ok_or_else(|| EngineError::param("bodies", "must be a list of body names"))?;
// Bounded before the duplicate check, which is quadratic in the list.
if names.len() > MAX_CELLS {
return Err(EngineError::Params(format!("`bodies` lists {} bodies; at most {MAX_CELLS} are meshed together", names.len())));
}
let mut out: Vec<&Shape> = Vec::with_capacity(names.len());
for n in names {
let name = n.as_str().ok_or_else(|| EngineError::Params("`bodies` must be a list of body names".into()))?;
let name = n.as_str().ok_or_else(|| EngineError::param("bodies", "must be a list of body names"))?;
let shape = design.body(name).ok_or_else(|| EngineError::Params(format!("no body `{name}`")))?;
if out.iter().any(|b| b.name == shape.name) {
return Err(EngineError::Params(format!("body `{name}` is listed twice")));
@@ -261,7 +259,7 @@ fn order(p: &Value) -> Result<bool> {
Some(v) => match v.as_u64() {
Some(1) => Ok(false),
Some(2) => Ok(true),
_ => Err(EngineError::Params("`order` must be 1 (hex8) or 2 (hex20)".into())),
_ => Err(EngineError::param("order", "must be 1 (hex8) or 2 (hex20)")),
},
}
}