app
Adom Chipsmith
Public Made by Adomby adom
STEP-native chip validator — 5-source cross-validation, OCCT-generated copyright-free 3D models with embedded signal annotations, datasheet-to-STEP pipeline.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
use crate::cli::{err, hint, ok, DEFAULT_PORT};
use crate::server::{self, convert, ServerConfig, ServerState};
use anyhow::{anyhow, Result};
use clap::Parser;
use std::path::PathBuf;
#[derive(Parser)]
pub struct Args {
/// Path to a .step or .stp file.
pub file: PathBuf,
#[arg(long, default_value_t = DEFAULT_PORT)]
pub port: u16,
/// Skip opening the Hydrogen webview tab (for headless test runs).
#[arg(long)]
pub no_open: bool,
#[arg(long, default_value = "STEP Viewer")]
pub tab_name: String,
#[arg(long, default_value = "mdi:cube-outline")]
pub display_icon: String,
}
pub fn run(args: Args) -> Result<()> {
let file = args.file.canonicalize().map_err(|e| {
anyhow!(
"{} does not exist: {e}",
args.file.display()
)
})?;
let ext = file
.extension()
.and_then(|s| s.to_str())
.map(|s| s.to_ascii_lowercase());
if !matches!(ext.as_deref(), Some("step") | Some("stp")) {
err(format!(
"{} is not a .step / .stp file",
file.display()
));
hint("adom-step is STEP-only; use the canonical 3D viewer for other formats");
return Err(anyhow!("not a STEP file"));
}
let bytes = std::fs::read(&file).map_err(|e| anyhow!("read {}: {e}", file.display()))?;
ok(format!(
"converting {} ({} KB) via service-step2glb...",
file.display(),
bytes.len() / 1024
));
let result = match convert::convert_step_bytes(&bytes) {
Ok(r) => r,
Err(e) => {
err(format!("conversion failed: {e}"));
return Err(e);
}
};
let label = if result.cache_hit {
"cache HIT"
} else {
"cache MISS"
};
ok(format!(
"{label} — GLB at {} ({} KB, {} meshes, {} nodes, {} ms)",
result.glb_path.display(),
result.size_bytes / 1024,
result.meshes,
result.nodes,
result.duration_ms
));
let label = file
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("model.step")
.to_string();
let config = ServerConfig {
port: args.port,
initial_glb_path: Some(result.glb_path.clone()),
initial_source_label: Some(label),
initial_source_type: Some("local-file".to_string()),
};
let state = ServerState::new(config);
let shutdown_state = state.clone();
ctrlc::set_handler(move || {
eprintln!("\nreceived SIGINT, shutting down adom-step");
let _ = &shutdown_state;
std::process::exit(0);
})?;
if !args.no_open {
// Open in a background thread after the server is up so the
// webview's first load resolves.
let port = args.port;
let tab_name = args.tab_name.clone();
let icon = args.display_icon.clone();
std::thread::spawn(move || {
std::thread::sleep(std::time::Duration::from_millis(400));
if let Err(e) =
crate::cli::open::open_webview_tab(port, &tab_name, &icon)
{
eprintln!("warn: failed to open webview tab: {e}");
}
});
}
ok(format!("adom-step running on port {}", args.port));
server::run(state)?;
Ok(())
}