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/detect.rsdeleted−179@@ -1,179 +0,0 @@-//! Find KiCad on this machine. Windows: registry (HKLM/HKCU SOFTWARE\KiCad\<ver>\InstallationPath)-//! plus the standard folders. Other OSes: PATH and the app bundle. No subprocess except-//! `kicad-cli version` for the exact patch level.--use std::path::{Path, PathBuf};--use serde::Serialize;--#[derive(Clone, Debug, Default, Serialize)]-pub struct KicadInstall {- pub version: String,- #[serde(rename = "baseDir")]- pub base_dir: String,- #[serde(rename = "binDir")]- pub bin_dir: String,- #[serde(rename = "kicadCli")]- pub kicad_cli: String,- #[serde(rename = "cliVersion")]- pub cli_version: String,- pub source: String,- #[serde(rename = "perUser")]- pub per_user: bool,-}--#[derive(Clone, Debug, Default, Serialize)]-pub struct KicadInfo {- pub installed: bool,- pub version: String,- pub installs: Vec<KicadInstall>,-}--impl KicadInfo {- pub fn summary(&self) -> String {- if !self.installed {- return "KiCad: not found (registry and standard folders checked)".into();- }- let i = &self.installs[0];- format!("KiCad {} at {} ({})", i.cli_version, i.base_dir, i.source)- }- pub fn primary(&self) -> Option<&KicadInstall> {- self.installs.first()- }-}--fn cli_name() -> &'static str {- if cfg!(windows) { "kicad-cli.exe" } else { "kicad-cli" }-}--fn probe_cli_version(cli: &Path) -> String {- let mut cmd = std::process::Command::new(cli);- cmd.arg("version");- crate::cli::quiet(&mut cmd);- match cmd.output() {- Ok(o) if o.status.success() => String::from_utf8_lossy(&o.stdout).trim().to_string(),- _ => String::new(),- }-}--fn install_from_base(base: &Path, version_hint: &str, source: &str, per_user: bool) -> Option<KicadInstall> {- let bin = base.join("bin");- let cli = bin.join(cli_name());- if !cli.is_file() {- return None;- }- let cli_version = probe_cli_version(&cli);- let version = if !version_hint.is_empty() {- version_hint.to_string()- } else {- cli_version.split('.').take(2).collect::<Vec<_>>().join(".")- };- Some(KicadInstall {- version,- base_dir: base.to_string_lossy().replace('\\', "/"),- bin_dir: bin.to_string_lossy().replace('\\', "/"),- kicad_cli: cli.to_string_lossy().replace('\\', "/"),- cli_version,- source: source.into(),- per_user,- })-}--fn version_key(v: &str) -> (u32, u32, u32) {- let mut it = v.split('.').map(|p| p.trim().parse::<u32>().unwrap_or(0));- (it.next().unwrap_or(0), it.next().unwrap_or(0), it.next().unwrap_or(0))-}--#[cfg(windows)]-fn registry_installs() -> Vec<KicadInstall> {- use winreg::enums::*;- use winreg::RegKey;- let mut out = Vec::new();- for (hive, name, per_user) in [(HKEY_LOCAL_MACHINE, "HKLM", false), (HKEY_CURRENT_USER, "HKCU", true)] {- let root = RegKey::predef(hive);- let Ok(k) = root.open_subkey("SOFTWARE\\KiCad") else { continue };- for ver in k.enum_keys().flatten() {- let Ok(vk) = k.open_subkey(&ver) else { continue };- let Ok(path): Result<String, _> = vk.get_value("InstallationPath") else { continue };- if let Some(i) = install_from_base(Path::new(&path), &ver, &format!("registry {name}"), per_user) {- out.push(i);- }- }- }- out-}--#[cfg(not(windows))]-fn registry_installs() -> Vec<KicadInstall> {- Vec::new()-}--fn folder_installs() -> Vec<KicadInstall> {- let mut out = Vec::new();- let mut bases: Vec<(PathBuf, bool)> = Vec::new();- if cfg!(windows) {- for (var, per_user) in [("ProgramFiles", false), ("ProgramFiles(x86)", false)] {- if let Ok(p) = std::env::var(var) {- bases.push((PathBuf::from(p).join("KiCad"), per_user));- }- }- let local = std::env::var("LOCALAPPDATA")- .map(PathBuf::from)- .or_else(|_| std::env::var("USERPROFILE").map(|u| PathBuf::from(u).join("AppData").join("Local")));- if let Ok(l) = local {- bases.push((l.join("Programs").join("KiCad"), true));- }- } else if cfg!(target_os = "macos") {- bases.push((PathBuf::from("/Applications/KiCad/KiCad.app/Contents/MacOS"), false));- } else {- bases.push((PathBuf::from("/usr"), false));- bases.push((PathBuf::from("/usr/local"), false));- }- for (base, per_user) in bases {- if cfg!(windows) {- let Ok(rd) = std::fs::read_dir(&base) else { continue };- for e in rd.flatten() {- let name = e.file_name().to_string_lossy().to_string();- if e.path().is_dir() && name.chars().next().map(|c| c.is_ascii_digit()).unwrap_or(false) {- if let Some(i) = install_from_base(&e.path(), &name, "folder", per_user) {- out.push(i);- }- }- }- } else if let Some(i) = install_from_base(&base, "", "folder", per_user) {- out.push(i);- }- }- out-}--pub fn detect() -> KicadInfo {- let mut installs = registry_installs();- for f in folder_installs() {- if !installs.iter().any(|i| i.bin_dir.eq_ignore_ascii_case(&f.bin_dir)) {- installs.push(f);- }- }- installs.sort_by(|a, b| version_key(&b.cli_version).cmp(&version_key(&a.cli_version)));- let installed = !installs.is_empty();- let version = installs.first().map(|i| i.cli_version.clone()).unwrap_or_default();- KicadInfo { installed, version, installs }-}--/// %APPDATA%/kicad/<major.minor>/ on Windows; ~/.config/kicad/<ver> elsewhere.-pub fn config_dir(version: &str) -> Option<PathBuf> {- let mm: String = version.split('.').take(2).collect::<Vec<_>>().join(".");- if cfg!(windows) {- let appdata = std::env::var("APPDATA").map(PathBuf::from).ok().or_else(|| {- std::env::var("USERPROFILE").ok().map(|u| PathBuf::from(u).join("AppData").join("Roaming"))- })?;- Some(appdata.join("kicad").join(mm))- } else if cfg!(target_os = "macos") {- Some(PathBuf::from(std::env::var("HOME").ok()?).join("Library/Preferences/kicad").join(mm))- } else {- let base = std::env::var("XDG_CONFIG_HOME")- .map(PathBuf::from)- .unwrap_or_else(|_| PathBuf::from(std::env::var("HOME").unwrap_or_default()).join(".config"));- Some(base.join("kicad").join(mm))- }-}