Coerce a CLI string value to the JSON type declared in the property schema. Falls back to a JSON string when the schema is absent or specifies an unknown type.
(key: &str, prop_schema: Option<&Value>, raw: &str)
| 576 | /// Falls back to a JSON string when the schema is absent or specifies an |
| 577 | /// unknown type. |
| 578 | fn coerce_value(key: &str, prop_schema: Option<&Value>, raw: &str) -> Result<Value> { |
| 579 | let ty = prop_schema |
| 580 | .and_then(|p| p.get("type")) |
| 581 | .and_then(Value::as_str) |
| 582 | .unwrap_or("string"); |
| 583 | |
| 584 | match ty { |
| 585 | "string" => Ok(Value::String(raw.to_string())), |
| 586 | "boolean" => match raw { |
| 587 | "true" | "1" | "yes" | "on" => Ok(Value::Bool(true)), |
| 588 | "false" | "0" | "no" | "off" => Ok(Value::Bool(false)), |
| 589 | other => { |
| 590 | let flag = key.replace('_', "-"); |
| 591 | Err(TraceDecayError::Config { |
| 592 | message: format!( |
| 593 | "--{flag}: expected a boolean (true/false), got `{other}` — \ |
| 594 | pass `--{flag} true` or `--{flag} false`" |
| 595 | ), |
| 596 | }) |
| 597 | } |
| 598 | }, |
| 599 | "integer" => raw |
| 600 | .parse::<i64>() |
| 601 | .map(Value::from) |
| 602 | .map_err(|_| TraceDecayError::Config { |
| 603 | message: format!("--{}: expected integer, got `{raw}`", key.replace('_', "-")), |
| 604 | }), |
| 605 | // `serde_json::Number::from_f64(25.0).as_u64()` returns `None`, so MCP |
| 606 | // handlers that read counts via `.as_u64()` would silently fall back |
| 607 | // to defaults. Prefer integer storage when the input is whole. |
| 608 | "number" => { |
| 609 | if let Ok(i) = raw.parse::<i64>() { |
| 610 | Ok(Value::from(i)) |
| 611 | } else { |
| 612 | raw.parse::<f64>() |
| 613 | .ok() |
| 614 | .and_then(serde_json::Number::from_f64) |
| 615 | .map(Value::Number) |
| 616 | .ok_or_else(|| TraceDecayError::Config { |
| 617 | message: format!( |
| 618 | "--{}: expected a finite number, got `{raw}`", |
| 619 | key.replace('_', "-") |
| 620 | ), |
| 621 | }) |
| 622 | } |
| 623 | } |
| 624 | // Array/object params accept inline JSON per-key (`--replacements |
| 625 | // '[["old","new"]]'`, `--project-selector '{"project_id":"x"}'`). |
| 626 | // Non-JSON strings fall through unchanged: arrays keep the |
| 627 | // comma-split/repetition behavior via `finalize_arrays`, and objects |
| 628 | // are caught by `validate_tool_args` with a corrective error. |
| 629 | "array" | "object" => { |
| 630 | if let Ok(parsed) = serde_json::from_str::<Value>(raw) { |
| 631 | if value_matches_type(&parsed, ty) { |
| 632 | return Ok(parsed); |
| 633 | } |
| 634 | } |
| 635 | Ok(Value::String(raw.to_string())) |
no test coverage detected