app
KiCad - the KiCad Bridge
Public Made by Adomby adom
Reference implementation of the KiCad bridge: multi-instance Python server, forward path via kicad-cli, reverse path via in-process plugin. Most complex of the three bundled bridges.
← Commit history
rust/src/main.rsdeleted−152@@ -1,152 +0,0 @@-//! Adom KiCad Bridge, native binary. Phase 1 of docs/rust-port-plan.md.-//!-//! Contract (same as the Python bridge and the hello-rust sample):-//! - `--port N` from ab, bind ADOM_BIND_HOST (never 0.0.0.0)-//! - GET /status -> health JSON with the optional led/summary/tooltip chip-//! - POST /command {"command":"kicad_x","args":{}} -> {"success","output"|"error",...}-//! Every verb lives in the catalog (catalog.rs) so describe, routing and risk-//! come from one table.--mod catalog;-mod cli;-mod detect;-mod verbs;--use std::time::Instant;--use serde::Deserialize;-use serde_json::{json, Value};-use tiny_http::{Header, Method, Response, Server, StatusCode};--pub const VERSION: &str = env!("CARGO_PKG_VERSION");-pub const SLUG: &str = "adom/kicad-bridge";--#[derive(Debug, Deserialize)]-struct Command {- #[serde(default)]- command: String,- #[serde(default)]- args: Value,-}--pub fn report_issues_line() -> String {- format!(- "THIS MAY BE A KICAD BRIDGE BUG. Check for a duplicate with `adom-wiki issue list {SLUG}`, \- then file: `adom-wiki issue create {SLUG} --title \"<symptom>\" --category bug-report \- --body \"<what you ran, expected, got, the verbatim response JSON, bridge version, OS>\"`. \- An ab-core problem (relay, lifecycle, identity) belongs on adom/adom-bridge instead."- )-}--pub struct Caller {- pub thread: String,- pub container: String,- pub reason: String,-}--fn header_value(req: &tiny_http::Request, name: &str) -> Option<String> {- req.headers()- .iter()- .find(|h| h.field.as_str().as_str().eq_ignore_ascii_case(name))- .map(|h| h.value.as_str().to_string())-}--fn parse_port_arg() -> Option<u16> {- let args: Vec<String> = std::env::args().collect();- let mut i = 1;- while i < args.len() {- if args[i] == "--port" && i + 1 < args.len() {- return args[i + 1].parse().ok();- }- if let Some(v) = args[i].strip_prefix("--port=") {- return v.parse().ok();- }- i += 1;- }- None-}--fn json_response(body: Value, code: u16) -> Response<std::io::Cursor<Vec<u8>>> {- let text = body.to_string();- Response::from_string(text)- .with_status_code(StatusCode(code))- .with_header(Header::from_bytes(&b"Content-Type"[..], &b"application/json"[..]).unwrap())-}--fn main() {- let port = parse_port_arg().unwrap_or(0);- let host = std::env::var("ADOM_BIND_HOST").unwrap_or_else(|_| "127.0.0.1".to_string());- let started = Instant::now();- let mut request_count: u64 = 0;- let mut verb_count: u64 = 0;-- let server = Server::http((host.as_str(), port))- .unwrap_or_else(|e| panic!("[kicad-bridge] bind {host}:{port} failed: {e}"));- let actual_port = server.server_addr().to_ip().map(|a| a.port()).unwrap_or(port);- eprintln!("[kicad-bridge] v{VERSION} listening on {host}:{actual_port}");-- let mut state = verbs::State::new();-- for mut req in server.incoming_requests() {- request_count += 1;- let uptime = started.elapsed().as_secs();- let (body, code) = match (req.method(), req.url()) {- (Method::Get, "/status") | (Method::Get, "/health") => {- let info = state.kicad_info();- let led = if info.installed { "green" } else { "yellow" };- (- json!({- "ok": true,- "bridge": "kicad",- "version": VERSION,- "runtime": "native",- "uptimeSec": uptime,- "requestCount": request_count,- "led": led,- "summary": if info.installed { format!("KiCad {}", info.version) } else { "KiCad not found".into() },- "tooltip": format!("kicad-bridge v{VERSION} (native)\nup {uptime}s, {request_count} requests\n{}", info.summary()),- }),- 200,- )- }- (Method::Post, "/command") => {- let caller = Caller {- thread: header_value(&req, "X-Adom-Caller-Thread").unwrap_or_else(|| "?".into()),- container: header_value(&req, "X-Adom-Caller-Container").unwrap_or_else(|| "?".into()),- reason: header_value(&req, "X-Adom-Caller-Reason").unwrap_or_default(),- };- let mut raw = String::new();- let _ = req.as_reader().read_to_string(&mut raw);- if raw.trim().is_empty() {- raw = "{}".into();- }- match serde_json::from_str::<Command>(&raw) {- Err(e) => (- json!({"success": false, "error": format!("bad JSON: {e}"), "_reportIssues": report_issues_line()}),- 400,- ),- Ok(cmd) => {- verb_count += 1;- eprintln!(- "[kicad-bridge] {} <- thread={} container={} reason={}",- cmd.command, caller.thread, caller.container, caller.reason- );- let t0 = Instant::now();- let mut out = verbs::dispatch(&mut state, &cmd.command, &cmd.args, &caller);- if let Some(o) = out.as_object_mut() {- o.insert("elapsedMs".into(), json!(t0.elapsed().as_millis() as u64));- o.entry("bridgeVersion").or_insert(json!(VERSION));- let failed = !o.get("success").and_then(Value::as_bool).unwrap_or(false);- if failed || verb_count == 1 {- o.insert("_reportIssues".into(), json!(report_issues_line()));- }- }- (out, 200)- }- }- }- _ => (json!({"success": false, "error": "not found"}), 404),- };- let _ = req.respond(json_response(body, code));- }-}