app
SOLIDWORKS Bridge
Public Made by Adomby adom
Launch, model and quit SOLIDWORKS from any Adom container. Nothing is ever lost.
main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
#![recursion_limit = "256"]
//! Adom SOLIDWORKS bridge (`swx`) — one binary, two modes.
//!
//! ```text
//! ab (Adom Bridge) ── HTTP /command ──> swx-bridge.exe (server mode)
//! │ JSON-lines over stdio
//! └─> swx-bridge.exe --com-host
//! (child process holding the live
//! SldWorks.Application COM object)
//! ```
//!
//! WHY a child process rather than an in-process COM thread: a wedged COM call
//! (SOLIDWORKS parked on a modal dialog, a rebuild that never returns) cannot be
//! killed as a thread — it can be killed as a process. The server owns the
//! child's lifecycle: lazy spawn, one request in flight, per-request timeout,
//! kill + respawn after a crash or a hang, stderr tail surfaced in `detail`.
//!
//! Non-Windows (or `SWX_MOCK=1`) swaps the real COM backend for the mock
//! backend, so the whole verb surface — routing, hints, error shapes, BOM/CSV
//! plumbing, the stdio protocol itself — is testable with no SOLIDWORKS and no
//! Windows.
//!
//! Bridge SDK contracts honored here: `--port N` off argv; bind ADOM_BIND_HOST
//! (loopback, never 0.0.0.0) FIRST, before anything slow, as the single-instance
//! guard; `/status` + `/health` with led/summary/tooltip; a rich `_hint` on every
//! verb response; `swx_uninstall` removes only what this bridge created; the
//! scratch dir is pruned; caller identity is logged (never trusted for auth).
pub mod abwin;
pub mod bom;
pub mod caller;
pub mod compat;
pub mod cswa;
pub mod comhost;
pub mod detect;
pub mod findfiles;
pub mod httpc;
pub mod jobs;
pub mod launch;
pub mod launchos;
pub mod modeling;
pub mod ledger;
pub mod memory;
pub mod owned;
pub mod perflog;
pub mod presence;
pub mod quit;
pub mod readiness;
pub mod render;
pub mod dialogs;
pub mod disk;
pub mod selftest;
pub mod scratch;
pub mod signin;
pub mod sha256;
pub mod script;
pub mod server;
pub mod session_history;
pub mod verbs;
pub mod verbs3;
pub mod visualize;
pub mod writes;
/// The bridge version ab reports and every response carries.
///
/// Cargo.toml is the single source of truth; the BRIDGE_VERSION file (which the
/// packaging scripts and bridge.json track) is checked against it AT COMPILE
/// TIME below, so the two can never drift apart unnoticed.
pub const BRIDGE_VERSION: &str = env!("CARGO_PKG_VERSION");
const _: () = {
const FILE_VERSION: &str = include_str!("../BRIDGE_VERSION");
const fn eq(a: &str, b: &str) -> bool {
let (a, b) = (a.as_bytes(), b.as_bytes());
if a.len() != b.len() {
return false;
}
let mut i = 0;
while i < a.len() {
if a[i] != b[i] {
return false;
}
i += 1;
}
true
}
assert!(
eq(BRIDGE_VERSION, FILE_VERSION.trim_ascii()),
"BRIDGE_VERSION file does not match the Cargo.toml package version — bump both together"
);
};
/// True when the bridge serves the mock backend instead of real COM: an
/// explicit `SWX_MOCK=1`, or any non-Windows host (where SOLIDWORKS cannot
/// exist). Server and com-host child agree because they read the same rule
/// from the same inherited environment.
pub fn is_mock() -> bool {
std::env::var("SWX_MOCK").as_deref() == Ok("1") || !cfg!(windows)
}
/// Entry point for both modes. `swx-bridge --com-host` becomes the COM child;
/// anything else is the HTTP bridge server.
pub fn run() {
let args: Vec<String> = std::env::args().skip(1).collect();
if args.iter().any(|a| a == "--com-host") {
comhost::host::run();
} else {
server::run(parse_port_arg(&args));
}
}
/// `--port N` / `--port=N` off argv. ab always passes it; the fallback only
/// matters when a developer runs the binary by hand.
pub fn parse_port_arg(args: &[String]) -> Option<u16> {
let mut it = args.iter();
while let Some(a) = it.next() {
if a == "--port" {
return it.next().and_then(|s| s.parse().ok());
}
if let Some(rest) = a.strip_prefix("--port=") {
return rest.parse().ok();
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn port_arg_forms() {
let s = |v: &[&str]| v.iter().map(|x| x.to_string()).collect::<Vec<_>>();
assert_eq!(parse_port_arg(&s(&["--port", "1234"])), Some(1234));
assert_eq!(parse_port_arg(&s(&["--port=99"])), Some(99));
assert_eq!(parse_port_arg(&s(&["--com-host"])), None);
assert_eq!(parse_port_arg(&s(&["--port", "not-a-port"])), None);
}
}