skill
Adom SF Convert
Public Made by Adomby adom
Native Rust converter: EAGLE/Fusion .lbr + Altium .SchLib/.PcbLib → KiCad, rendering each format's own geometry.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
//! adom-sfconvert — convert foreign EDA libraries into standard KiCad symbol +
//! footprint, so the Adom EDA family (adom-symbol / adom-footprint) can render
//! and compare them. Named like ds2sf: "sf" = symbol + footprint.
//!
//! Supported today:
//! • EAGLE / Fusion 360 .lbr → KiCad (native, faithful — renders own geometry)
//! Detected but not yet converted (honest):
//! • Altium .SchLib / .PcbLib → needs KiCad's Altium importer (OLE binary)
mod altium;
mod eagle;
use anyhow::{anyhow, Context, Result};
use std::path::PathBuf;
fn main() {
if let Err(e) = run() {
eprintln!("ERROR: {e:#}");
std::process::exit(1);
}
}
fn usage() -> ! {
eprintln!(
"adom-sfconvert {}\n\n\
USAGE:\n \
adom-sfconvert convert <library-file> [--out-dir <dir>] [--name <name>]\n \
adom-sfconvert version\n\n\
Converts EAGLE/Fusion 360 .lbr (and, soon, Altium .SchLib/.PcbLib) into\n \
<name>.kicad_sym + <name>.kicad_mod — the standard format every Adom EDA\n \
app renders. Original files are never modified.",
env!("CARGO_PKG_VERSION")
);
std::process::exit(2);
}
fn run() -> Result<()> {
let args: Vec<String> = std::env::args().skip(1).collect();
match args.first().map(String::as_str) {
Some("version") | Some("--version") | Some("-V") => {
println!("adom-sfconvert {}", env!("CARGO_PKG_VERSION"));
Ok(())
}
Some("convert") => convert(&args[1..]),
_ => usage(),
}
}
fn convert(args: &[String]) -> Result<()> {
let mut file: Option<PathBuf> = None;
let mut out_dir: Option<PathBuf> = None;
let mut name: Option<String> = None;
let mut i = 0;
while i < args.len() {
match args[i].as_str() {
"--out-dir" => { out_dir = args.get(i + 1).map(PathBuf::from); i += 2; }
"--name" => { name = args.get(i + 1).cloned(); i += 2; }
s if file.is_none() => { file = Some(PathBuf::from(s)); i += 1; }
_ => i += 1,
}
}
let file = file.ok_or_else(|| anyhow!("convert needs a library file"))?;
let out_dir = out_dir.unwrap_or_else(|| file.parent().map(PathBuf::from).unwrap_or_else(|| PathBuf::from(".")));
let stem = name.unwrap_or_else(|| file.file_stem().map(|s| s.to_string_lossy().to_string()).unwrap_or_else(|| "part".into()));
let ext = file.extension().and_then(|e| e.to_str()).unwrap_or("").to_lowercase();
let bytes = std::fs::read(&file).with_context(|| format!("reading {}", file.display()))?;
// Altium = OLE compound binary (magic d0 cf 11 e0). Read it NATIVELY with
// altium-schlib / altium-pcblib — pure Rust, no KiCad, no service round-trip.
// Emits the native SVG thumbnail + .kicad_sym/.kicad_mod (export).
let is_ole = bytes.starts_with(&[0xD0, 0xCF, 0x11, 0xE0]);
if ext == "schlib" || ext == "pcblib" || ext == "intlib" || is_ole {
let out = altium::convert(&file, &out_dir, &stem, &ext, true)?;
println!("OK: {} (Altium → KiCad, native) → {}", file.display(), out.files.join(" + "));
return Ok(());
}
let text = String::from_utf8_lossy(&bytes);
if ext == "lbr" || text.contains("<eagle") {
let (sym, modf) = eagle::convert(&text, &stem)?;
std::fs::create_dir_all(&out_dir).ok();
let mut wrote = Vec::new();
if !sym.is_empty() {
let p = out_dir.join(format!("{stem}.kicad_sym"));
std::fs::write(&p, &sym).with_context(|| format!("writing {}", p.display()))?;
wrote.push(p.display().to_string());
}
if !modf.is_empty() {
let p = out_dir.join(format!("{stem}.kicad_mod"));
std::fs::write(&p, &modf).with_context(|| format!("writing {}", p.display()))?;
wrote.push(p.display().to_string());
}
// Native SVG thumbnails — drawn straight from the EAGLE geometry, no
// KiCad round-trip (same as the Altium path).
let sym_svg = eagle::render_symbol_svg(&text, &stem);
if !sym_svg.is_empty() {
let p = out_dir.join(format!("{stem}-symbol.svg"));
std::fs::write(&p, &sym_svg).ok();
wrote.push(p.display().to_string());
}
let fp_svg = eagle::render_footprint_svg(&text, &stem);
if !fp_svg.is_empty() {
let p = out_dir.join(format!("{stem}-footprint.svg"));
std::fs::write(&p, &fp_svg).ok();
wrote.push(p.display().to_string());
}
if wrote.is_empty() {
return Err(anyhow!("EAGLE library had no <symbol> or <package> to convert"));
}
println!("OK: {} (EAGLE/Fusion, native) → {}", file.display(), wrote.join(" + "));
return Ok(());
}
Err(anyhow!("unrecognized library format: {} (expected .lbr / .SchLib / .PcbLib)", file.display()))
}