Skip to content

Replace positional WASM cost-binding parameters with a struct argument #190

@ecto

Description

@ecto

Problem

estimate_cost_for_process in crates/vcad-kernel-wasm/src/lib.rs (added on the v1 DFM branch) takes six positional parameters:

pub fn estimate_cost_for_process(
    process: &str,
    material_name: &str,
    part_volume_mm3: f64,
    stock_volume_mm3: f64,
    qty: u32,
    feature_count: u32,
) -> Result<JsValue, JsError>

with magic-value semantics: stock_volume_mm3 = 0 means "use 2× part volume", qty = 0 means "use rule-pack default". TS callers can't tell from the signature which params are optional or what the sentinel values mean — the engine wrapper at packages/engine/src/dfm.ts::estimateCost papers over this with a ?? 0 fallback per arg.

Same critique applies to Solid.runDfm(process, rule_pack_toml, root_node_id)root_node_id = 0 is the "skip provenance" sentinel.

Proposed approach

Take a single struct argument deserialized via serde_wasm_bindgen::from_value:

#[derive(Deserialize)]
struct EstimateCostRequest {
    process: String,
    material: String,
    part_volume_mm3: f64,
    #[serde(default)]
    stock_volume_mm3: Option<f64>,
    #[serde(default)]
    qty: Option<u32>,
    #[serde(default)]
    feature_count: Option<u32>,
}

#[wasm_bindgen]
pub fn estimate_cost_for_process(req: JsValue) -> Result<JsValue, JsError> {
    let req: EstimateCostRequest = serde_wasm_bindgen::from_value(req)?;
    // ...
}

Optionality becomes a first-class concept via Option<T>, the engine wrapper can omit fields cleanly, and the .d.ts generated by ts-rs (or the hand-written types in packages/engine/src/dfm.ts) reflects optionality directly.

Why now

The DFM PR introduced this pattern; cleaning it up before more callers latch onto the positional shape is much cheaper than later. Same fix applied to Solid.runDfm and run_dfm_on_brep_json keeps the new DFM surface consistent.

Acceptance criteria

  • estimate_cost_for_process, Solid.runDfm, and run_dfm_on_brep_json accept a single struct argument.
  • packages/engine/src/dfm.ts EstimateCostOptions / RunDfmOptions interfaces line up directly with the Rust structs (no ?? 0 paper-over).
  • TS consumers stop having to know the sentinel values.
  • The MCP tools and the QuotePanel keep working unchanged behaviorally.

References

  • crates/vcad-kernel-wasm/src/lib.rs — three new functions added on claude/dfm-tools-integration-HcsqQ: estimate_cost_for_process, run_dfm_on_brep_json, Solid.runDfm
  • packages/engine/src/dfm.ts::estimateCost — current TS wrapper that spreads positionals
  • The existing evaluateDocument WASM binding does take positional (docJson, skipClashDetection) args and is happy — so this isn't a hard rule across the crate, just the right one for cost/DFM where optionality is real

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions