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 + footprint + 3D + pin↔pad map. Altium is written natively (no Altium needed). Includes the adom-lbr Manager: browse wiki components and view symbol/footprint/3D + the per-EDA layer stackup.
← Commit history
2.22.2: negative-controlled regression tests
7 files changed
+97−6
Cargo.lock+1−1@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "adom-lbr"-version = "2.22.1"+version = "2.22.2" dependencies = [ "altium-codec", "anyhow",
Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "adom-lbr"-version = "2.22.1"+version = "2.22.2" edition = "2021" description = "adom-lbr generator / EDA translator: the canonical adom-lbr JSON is the hub, with native converters to Altium (.SchLib/.PcbLib/.IntLib), EAGLE/Fusion (.lbr), and KiCad. Altium output is written natively — no Altium needed at conversion time."
VERSION+1−1@@ -1 +1 @@-2.22.1+2.22.2
bin/adom-lbr⋯ 1 unchanged line ⋯
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "adom-lbr",- "version": "2.22.1",+ "version": "2.22.2", "type": "app", "description": "adom-lbr \u2014 the EDA library translator. Bring a component in from any supported EDA tool and convert it to any other (KiCad \u21c4 Altium \u21c4 EAGLE/Fusion) through one canonical adom-lbr JSON \u2014 symbol + footprint + 3D + pin\u2194pad map. Altium is written natively (no Altium needed). Includes the adom-lbr Manager: browse wiki components and view symbol/footprint/3D + the per-EDA layer stackup.", "dependencies": {},
page.json+2−2@@ -1,6 +1,6 @@ { "slug": "adom-lbr",- "version": "2.22.1",+ "version": "2.22.2", "type": "app", "description": "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 + footprint + 3D + pin↔pad map. Altium is written natively (no Altium needed). Includes the adom-lbr Manager: browse wiki components and view symbol/footprint/3D + the per-EDA layer stackup.", "dependencies": {},@@ -88,4 +88,4 @@ "label": "Browse parts" } ]-}+}
src/main.rs+91@@ -3060,6 +3060,97 @@ fn cmd_embed(dir: &std::path::Path, out: Option<PathBuf>) -> Result<(), String> mod lint_tests { use super::{dedup_pad_names, lint_lbr}; + // ---- 2.21.0 / 2.22.x conversion fidelity -------------------------------------+ // Every one of these guards a bug that reached a user's Fusion install, so each test+ // names the symptom rather than the function.++ #[test]+ fn kicad_pin_length_maps_to_the_nearest_eagle_stop_not_the_next_one_up() {+ use super::pin_length_keyword;+ // 2.794 is the standard 0.11 in pin on the wiki's passives. The old ceiling rule+ // sent it to "middle" (5.08), so a two-pin part drew as one line with its pins+ // overlapping by 2.54 mm.+ assert_eq!(pin_length_keyword(2.794), "short");+ assert_eq!(pin_length_keyword(0.0), "point");+ assert_eq!(pin_length_keyword(2.54), "short");+ assert_eq!(pin_length_keyword(5.08), "middle");+ assert_eq!(pin_length_keyword(7.62), "long");+ assert_eq!(pin_length_keyword(20.0), "long");+ // a value nearer the upper stop still rounds up+ assert_eq!(pin_length_keyword(4.9), "middle");+ }++ #[test]+ fn unnamed_pins_take_the_pad_number_and_a_pin_really_called_p_survives() {+ use super::{dedup_pin_names, KicadPin};+ let pin = |name: &str, number: &str| KicadPin {+ name: name.to_string(), number: number.to_string(),+ x: 0.0, y: 0.0, angle: 0, length: 2.54, pin_type: "passive".to_string(),+ };+ // "~" is KiCad's unnamed marker; it used to sanitise to the literal "P", so an+ // unnamed two-pin part shipped pins called "P" and "P@2".+ let got = dedup_pin_names(&[pin("~", "1"), pin("~", "2")]);+ assert_eq!(got, vec!["1".to_string(), "2".to_string()]);+ // an empty name behaves the same way+ assert_eq!(dedup_pin_names(&[pin("", "7")]), vec!["7".to_string()]);+ // ...but a pin genuinely named P keeps its name+ assert_eq!(dedup_pin_names(&[pin("P", "3")]), vec!["P".to_string()]);+ // and real names are untouched, including the @-suffix for a repeat+ assert_eq!(dedup_pin_names(&[pin("GND", "4"), pin("GND", "5")]),+ vec!["GND".to_string(), "GND@2".to_string()]);+ }++ #[test]+ fn catalog_boilerplate_is_stripped_without_mangling_decimals() {+ use super::strip_catalog_boilerplate;+ assert_eq!(+ strip_catalog_boilerplate("WCH USB Converters, SOP-16. Datasheet-verified pinout, KiCad symbol/footprint/3D."),+ "WCH USB Converters, SOP-16");+ // the first version split on every '.' and rejoined, turning a voltage into "2. 5V"+ assert_eq!(strip_catalog_boilerplate("2.5V precision reference"), "2.5V precision reference");+ assert_eq!(strip_catalog_boilerplate("STM32F103. 72 MHz Cortex-M3"), "STM32F103. 72 MHz Cortex-M3");+ // nothing but boilerplate: keep the original rather than emit an empty description+ assert!(!strip_catalog_boilerplate("Datasheet-verified pinout").is_empty());+ }++ #[test]+ fn package_description_leads_with_geometry_and_never_states_a_bad_measurement() {+ use super::package_description;+ // Fusion's Description column shows ~20 characters, so the identifying words go first.+ assert_eq!(package_description(None, "SOIC-8_3.9x4.9mm_P1.27mm"),+ "SOIC-8, 3.9 x 4.9 mm, 1.27 mm pitch");+ // a descr that merely restates the geometry is dropped rather than appended+ let restating = r#"(footprint "SOIC-8_3.9x4.9mm_P1.27mm" (descr "SOIC, 8 Pin"))"#;+ assert_eq!(package_description(Some(restating), "SOIC-8_3.9x4.9mm_P1.27mm"),+ "SOIC-8, 3.9 x 4.9 mm, 1.27 mm pitch");+ // a descr that adds something is appended AFTER the geometry+ let adds = r#"(footprint "SOIC-8_3.9x4.9mm_P1.27mm" (descr "Analog Devices precision reference"))"#;+ assert!(package_description(Some(adds), "SOIC-8_3.9x4.9mm_P1.27mm")+ .starts_with("SOIC-8, 3.9 x 4.9 mm, 1.27 mm pitch. "));+ // a URL in the descr does not leave the bracket that introduced it behind+ let urly = r#"(footprint "X" (descr "SOIC, 8 Pin (JEDEC MS-012AA, https://example.com/x.pdf)"))"#;+ let out = package_description(Some(urly), "X");+ assert!(!out.contains("http"), "{out}");+ assert!(!out.ends_with('('), "{out}");+ assert!(out.matches('(').count() == out.matches(')').count(), "unbalanced: {out}");+ }++ #[test]+ fn a_footprint_named_by_mpn_is_measured_rather_than_repeating_its_own_name() {+ use super::package_description;+ // Two 1.0 x 0.5 mm pads 1.0 mm apart: a plain chip land pattern.+ let km = r#"(footprint "0603N101J500CT"+ (pad "1" smd rect (at -0.75 0) (size 1.0 0.5) (layers "F.Cu"))+ (pad "2" smd rect (at 0.75 0) (size 1.0 0.5) (layers "F.Cu")))"#;+ let out = package_description(Some(km), "0603N101J500CT");+ assert!(out.starts_with("2 pads,"), "{out}");+ assert!(!out.contains("0603N101J500CT"), "must not repeat the Name column: {out}");+ // The first version measured the SILKSCREEN, which on a chip part is two ticks, and+ // reported "0.6 x 0.0 mm". A confidently wrong measurement is worse than none.+ assert!(!out.contains("0.0 mm"), "{out}");+ assert!(out.contains("2.5 x 0.5 mm"), "expected the copper extent: {out}");+ }+ #[test] fn four_sided_symbol_relays_into_two_columns() { use super::{relayout_two_columns, KicadPin, ParsedSymbol};