app
Adom Library
Public Made by Adomby adom
adom-lbr — the EDA library translator. Bring a component in from any supported EDA tool and convert it to any other (KiCad ⇄ Altium ⇄ EAGLE/Fusion) through one canonical adom-lbr JSON — symbol + footp
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
//! altium-codec CLI — encode neutral-model JSON to Altium libraries, or decode.
//!
//! Usage:
//! altium-codec encode-sym <symbols.json> <out.SchLib>
//! altium-codec encode-fp <footprint.json> <out.PcbLib>
//! altium-codec decode <in.SchLib|in.PcbLib>
//!
//! symbols.json: a JSON array of Symbol, or a single Symbol object.
//! footprint.json: a single Footprint object.
use altium_codec::{
decode_pcblib, decode_schlib_first, encode_pcblib, encode_schlib, import_kicad, Footprint,
Symbol,
};
use anyhow::{bail, Context, Result};
use std::fs;
use std::path::Path;
/// Seed a symbol's Altium property fields from the part identity, unless the
/// adom-lbr json already carries a same-named parameter. `Value` is drawn on the
/// symbol; identity metadata (Manufacturer, MPN, Package) is added hidden.
fn add_part_parameters(symbol: &mut altium_codec::Symbol, part: &altium_codec::AdomLbrPart) {
use altium_codec::Parameter;
let mut push = |name: &str, value: &str, visible: bool| {
if value.is_empty() {
return;
}
if symbol.parameters.iter().any(|p| p.name.eq_ignore_ascii_case(name)) {
return;
}
symbol.parameters.push(Parameter { name: name.into(), value: value.into(), visible });
};
push("Value", &part.value, true);
push("Manufacturer", &part.manufacturer, false);
push("Manufacturer Part Number", &part.mpn, false);
push("Package", &part.package, false);
}
fn main() -> Result<()> {
let args: Vec<String> = std::env::args().collect();
if args.len() < 3 {
eprintln!("usage: altium-codec <encode-sym|encode-fp|decode> <in> [out]");
std::process::exit(2);
}
match args[1].as_str() {
"encode-sym" => {
let json = fs::read_to_string(&args[2]).context("read symbols json")?;
let syms: Vec<Symbol> = match serde_json::from_str::<Vec<Symbol>>(&json) {
Ok(v) => v,
Err(_) => vec![serde_json::from_str::<Symbol>(&json).context("parse Symbol")?],
};
let bytes = encode_schlib(&syms)?;
let out = args.get(3).map(|s| s.as_str()).unwrap_or("out.SchLib");
fs::write(out, &bytes)?;
println!("wrote {} ({} bytes, {} symbols)", out, bytes.len(), syms.len());
}
"encode-fp" => {
let json = fs::read_to_string(&args[2]).context("read footprint json")?;
let fp: Footprint = serde_json::from_str(&json).context("parse Footprint")?;
let bytes = encode_pcblib(&fp)?;
let out = args.get(3).map(|s| s.as_str()).unwrap_or("out.PcbLib");
fs::write(out, &bytes)?;
println!("wrote {} ({} bytes, {} pads)", out, bytes.len(), fp.pads.len());
}
"decode" => {
let p = Path::new(&args[2]);
let bytes = fs::read(p).context("read input")?;
let ext = p.extension().and_then(|e| e.to_str()).unwrap_or("").to_lowercase();
let v = if ext.contains("pcblib") {
decode_pcblib(&bytes)?
} else {
decode_schlib_first(&bytes)?
};
println!("{}", serde_json::to_string_pretty(&v)?);
}
// kicad sym+mod → universal part.json (the import / hub step)
"import-kicad" => {
let md_path = args
.get(3)
.ok_or_else(|| anyhow::anyhow!("usage: altium-codec import-kicad <in.kicad_sym> <in.kicad_mod> [mpn]"))?;
let sym = fs::read_to_string(&args[2]).context("read kicad_sym")?;
let md = fs::read_to_string(md_path).context("read kicad_mod")?;
let mpn = args.get(4).cloned().unwrap_or_else(|| "PART".into());
let part = import_kicad(&sym, &md, &mpn)?;
let json = serde_json::to_string_pretty(&part)?;
println!("{json}");
}
// universal part.json → Altium .SchLib + .PcbLib (the export step)
"export-altium" => {
let json = fs::read_to_string(&args[2]).context("read part.json")?;
let part: altium_codec::AdomLbrPart =
serde_json::from_str(&json).context("parse AdomLbrPart")?;
let outdir = args.get(3).map(|s| s.as_str()).unwrap_or(".");
let mut symbol = part.symbol.clone();
add_part_parameters(&mut symbol, &part);
let sym_bytes = encode_schlib(&[symbol])?;
let fp_bytes = encode_pcblib(&part.footprint)?;
let sp = format!("{outdir}/{}.SchLib", part.mpn);
let pp = format!("{outdir}/{}.PcbLib", part.mpn);
fs::write(&sp, &sym_bytes)?;
fs::write(&pp, &fp_bytes)?;
println!(
"wrote {sp} ({} pins) + {pp} ({} pads)",
part.symbol.pins.len(),
part.footprint.pads.len()
);
}
// universal part.json → one self-contained Altium .IntLib
"export-intlib" => {
let json = fs::read_to_string(&args[2]).context("read part.json")?;
let part: altium_codec::AdomLbrPart =
serde_json::from_str(&json).context("parse AdomLbrPart")?;
let descr = if part.value.is_empty() { part.mpn.clone() } else { format!("{} {}", part.value, part.package) };
let mut symbol = part.symbol.clone();
add_part_parameters(&mut symbol, &part);
let bytes = altium_codec::encode_intlib(&symbol, &part.footprint, &descr)?;
let out = args.get(3).cloned().unwrap_or_else(|| format!("{}.IntLib", part.mpn));
fs::write(&out, &bytes)?;
println!("wrote {out} ({} bytes) — symbol {} + footprint {} bundled",
bytes.len(), part.symbol.name, part.footprint.name);
}
// Add a part's symbol into an EXISTING .SchLib (non-destructive merge):
// altium-codec add-to-schlib <existing.SchLib> <part.json> <out.SchLib>
"add-to-schlib" => {
let json_path = args
.get(3)
.ok_or_else(|| anyhow::anyhow!("usage: altium-codec add-to-schlib <existing.SchLib> <part.json> [out.SchLib]"))?;
let existing = fs::read(&args[2]).context("read existing SchLib")?;
let json = fs::read_to_string(json_path).context("read part.json")?;
// Accept an AdomLbrPart, a bare Symbol, or an array of Symbols.
let syms: Vec<Symbol> = if let Ok(p) =
serde_json::from_str::<altium_codec::AdomLbrPart>(&json)
{
vec![p.symbol]
} else if let Ok(v) = serde_json::from_str::<Vec<Symbol>>(&json) {
v
} else {
vec![serde_json::from_str::<Symbol>(&json).context("parse Symbol/AdomLbrPart")?]
};
let merged = altium_codec::add_symbols_to_schlib(&existing, &syms)?;
let out = args.get(4).map(|s| s.as_str()).unwrap_or("merged.SchLib");
fs::write(out, &merged)?;
println!(
"wrote {out} ({} bytes) — added {} symbol(s) into existing library",
merged.len(),
syms.len()
);
}
other => bail!("unknown command: {other}"),
}
Ok(())
}