BREP_app 0.4.0

The BREP CAD application: an eframe (egui + wgpu) host that draws the brep-render 3D engine into an egui frame — native + wasm from one codebase.
Documentation
//! The WIRE HARNESS workbench: the connections between ports, and the routing
//! report the run produces.
//!
//! A connection names two PORT features and a wire diameter; the runner routes
//! it through the harness segments and (when bundles are on) builds the bundle
//! solids. These are the operations `panels::wire_harness` performs, on the
//! same engine methods.
use crate::automation::command::{parse_args, schema_of, Annotations, CommandSpec, Ctx, Empty, Handler, NoArgs, Outcome, Phase};
use crate::automation::cmd_document::parse;
use brep_render::engine_state::ConnectionPatch;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};

#[derive(Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct AddConnectionArgs {
    /// The A-side port feature id (`wire_harness_state`'s report lists the
    /// endpoints); empty leaves it unassigned.
    #[serde(default)]
    pub from: String,
    /// The B-side port feature id; empty leaves it unassigned.
    #[serde(default)]
    pub to: String,
    /// Wire diameter in mm. Non-positive falls back to 1.0.
    #[serde(default = "one")]
    pub diameter: f64,
}

fn one() -> f64 {
    1.0
}

#[derive(Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct UpdateConnectionArgs {
    pub id: String,
    #[serde(default)]
    pub name: Option<String>,
    #[serde(default)]
    pub from: Option<String>,
    #[serde(default)]
    pub to: Option<String>,
    /// Must be positive; a non-positive diameter is refused.
    #[serde(default)]
    pub diameter: Option<f64>,
}

#[derive(Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ConnectionIdArgs {
    pub id: String,
}

#[derive(Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct BuildBundlesArgs {
    /// Build the bundle solids. The routing report is produced either way.
    pub on: bool,
}

#[derive(Serialize, schemars::JsonSchema)]
pub struct ConnectionAdded {
    /// The id the engine minted (`wire-N`).
    pub id: String,
}

fn wire_harness_state(ctx: &mut Ctx<'_>, args: Value) -> Result<Outcome, String> {
    let _: NoArgs = parse_args(args)?;
    Ok(Outcome::Done(parse(&ctx.app.docs.engine().wire_harness_state_json())))
}

fn wire_harness_add_connection(ctx: &mut Ctx<'_>, args: Value) -> Result<Outcome, String> {
    let a: AddConnectionArgs = parse_args(args)?;
    let id = ctx.app.docs.engine_mut().wire_harness_add_connection(&a.from, &a.to, a.diameter);
    serde_json::to_value(ConnectionAdded { id }).map(Outcome::Done).map_err(|e| e.to_string())
}

fn wire_harness_update_connection(ctx: &mut Ctx<'_>, args: Value) -> Result<Outcome, String> {
    let a: UpdateConnectionArgs = parse_args(args)?;
    let patch = ConnectionPatch { name: a.name, from: a.from, to: a.to, diameter: a.diameter };
    ctx.app.docs.engine_mut().wire_harness_update_connection(&a.id, &patch)?;
    Ok(Outcome::Done(json!({})))
}

fn wire_harness_remove_connection(ctx: &mut Ctx<'_>, args: Value) -> Result<Outcome, String> {
    let a: ConnectionIdArgs = parse_args(args)?;
    ctx.app.docs.engine_mut().wire_harness_remove_connection(&a.id)?;
    Ok(Outcome::Done(json!({})))
}

fn wire_harness_set_build_bundles(ctx: &mut Ctx<'_>, args: Value) -> Result<Outcome, String> {
    let a: BuildBundlesArgs = parse_args(args)?;
    ctx.app.docs.engine_mut().wire_harness_set_build_bundles(a.on);
    Ok(Outcome::Done(json!({})))
}

pub static COMMANDS: &[CommandSpec] = &[
    CommandSpec { name: "wire_harness_state", group: "harness", doc: "The document's harness block: every connection, whether bundle solids are built, and the last run's routing report (endpoints, segments, per-connection routes, bundles).", phase: Phase::Read, annotations: Annotations::READ, args_schema: schema_of::<NoArgs>, result_schema: schema_of::<Empty>, handler: Handler::App(wire_harness_state) },
    CommandSpec { name: "wire_harness_add_connection", group: "harness", doc: "Add a wire between two port features and re-run the routing. Either end may be empty and filled in later. Returns the connection id.", phase: Phase::Mutate, annotations: Annotations::MUTATE, args_schema: schema_of::<AddConnectionArgs>, result_schema: schema_of::<ConnectionAdded>, handler: Handler::App(wire_harness_add_connection) },
    CommandSpec { name: "wire_harness_update_connection", group: "harness", doc: "Patch one connection's name, ends or diameter; absent fields are unchanged.", phase: Phase::Mutate, annotations: Annotations::MUTATE, args_schema: schema_of::<UpdateConnectionArgs>, result_schema: schema_of::<Empty>, handler: Handler::App(wire_harness_update_connection) },
    CommandSpec { name: "wire_harness_remove_connection", group: "harness", doc: "Delete one connection and re-run the routing.", phase: Phase::Mutate, annotations: Annotations::DESTRUCTIVE, args_schema: schema_of::<ConnectionIdArgs>, result_schema: schema_of::<Empty>, handler: Handler::App(wire_harness_remove_connection) },
    CommandSpec { name: "wire_harness_set_build_bundles", group: "harness", doc: "Turn the bundle solids on or off. Off still routes and reports — it just builds no geometry.", phase: Phase::Mutate, annotations: Annotations::MUTATE, args_schema: schema_of::<BuildBundlesArgs>, result_schema: schema_of::<Empty>, handler: Handler::App(wire_harness_set_build_bundles) },
];