app
KiCad - the KiCad Bridge
Public Made by Adomby adom
Drive your own KiCad from an AI, in the background: open every editor, read the whole board as data (the EDA Bridge Standard board model), stack-up, part heights, netlists, place, route, pour, DRC and exports.
master
John Lauer
PCM: the wiki page as a KiCad PCM repository. Sample library package (10 symbols, 10 footprints) under pcm/src, tools/b…
9ea1ed3
1mo ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
//! The sample PCM package under pcm/src/adom-sample-lib (the wiki page acting as a KiCad
//! PCM repository, docs/pcm.md): the ten symbols and ten footprints parse with the same
//! structural checks kicad_lint_library runs, the packed archive under pcm/packages/
//! matches packages.json byte for byte (sha256, size), and the bridge's own zip reader
//! lays it out under the 3rdparty root exactly as PCM's extract() would.
use std::path::{Path, PathBuf};
use kicad_core::lint::{check_footprint_structure, check_symbol_lib_structure, parse_kicad_mod, parse_kicad_sym};
use kicad_core::pcm::{extract, sha256_hex};
fn repo(rel: &str) -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("../../..").join(rel)
}
#[test]
fn sample_symbols_and_footprints_parse() {
let sym_path = repo("pcm/src/adom-sample-lib/symbols/Adom_Sample.kicad_sym");
let text = std::fs::read_to_string(&sym_path).expect("symbol library readable");
check_symbol_lib_structure(&text).expect("balanced kicad_symbol_lib");
let inv = parse_kicad_sym(&text);
assert_eq!(inv["symbolCount"], 10, "{inv}");
let names: Vec<&str> = inv["symbols"].as_array().unwrap().iter().map(|s| s["name"].as_str().unwrap()).collect();
for n in ["AdomSample_R", "AdomSample_C", "AdomSample_L", "AdomSample_D", "AdomSample_LED", "AdomSample_C_Polarized", "AdomSample_D_Zener", "AdomSample_Fuse", "AdomSample_Crystal", "AdomSample_Battery_Cell"] {
assert!(names.contains(&n), "missing {n} in {names:?}");
}
for s in inv["symbols"].as_array().unwrap() {
assert!(s["pinCount"].as_u64().unwrap() >= 2, "{s}");
}
assert_eq!(inv["fileVersion"], 20251024, "KiCad 10 symbol format");
assert!(!text.contains("(extends"), "every sample symbol is a base symbol");
let pretty = repo("pcm/src/adom-sample-lib/footprints/Adom_Sample.pretty");
let mut mods: Vec<PathBuf> = std::fs::read_dir(&pretty).unwrap().flatten().map(|e| e.path()).filter(|p| p.extension().map(|x| x == "kicad_mod").unwrap_or(false)).collect();
mods.sort();
assert_eq!(mods.len(), 10);
for m in &mods {
let t = std::fs::read_to_string(m).unwrap();
check_footprint_structure(&t).unwrap_or_else(|e| panic!("{}: {e}", m.display()));
let p = parse_kicad_mod(&t);
let stem = m.file_stem().unwrap().to_string_lossy().to_string();
assert_eq!(p["name"], serde_json::json!(stem), "declared name matches the file stem");
assert!(stem.starts_with("AdomSample_"));
assert!(p["padCount"].as_u64().unwrap() >= 2, "{}: {p}", m.display());
}
}
#[test]
fn packed_archive_matches_packages_json_and_extracts_like_pcm() {
let packages: serde_json::Value = serde_json::from_str(&std::fs::read_to_string(repo("pcm/packages.json")).unwrap()).unwrap();
let repository: serde_json::Value = serde_json::from_str(&std::fs::read_to_string(repo("pcm/repository.json")).unwrap()).unwrap();
// repository.json's digest of packages.json is the digest of the file on disk.
let packages_bytes = std::fs::read(repo("pcm/packages.json")).unwrap();
assert_eq!(repository["packages"]["sha256"], serde_json::json!(sha256_hex(&packages_bytes)));
let pkg = &packages["packages"][0];
assert_eq!(pkg["identifier"], "com.adom.kicad-bridge.sample-lib");
let ident = pkg["identifier"].as_str().unwrap();
for v in pkg["versions"].as_array().unwrap() {
let url = v["download_url"].as_str().unwrap();
let file = url.rsplit('/').next().unwrap();
let zip = std::fs::read(repo("pcm/packages").join(file)).unwrap_or_else(|e| panic!("{file}: {e}"));
assert_eq!(v["download_size"], serde_json::json!(zip.len()));
assert_eq!(v["download_sha256"], serde_json::json!(sha256_hex(&zip)));
let third = std::env::temp_dir().join(format!("kicad-core-pcm-sample-{}-{}", std::process::id(), v["version"].as_str().unwrap()));
let _ = std::fs::remove_dir_all(&third);
let e = extract(&zip, &third, ident).expect("the bridge's zip reader handles the packer's deflate output");
assert_eq!(e.package_dir, "com_adom_kicad-bridge_sample-lib");
assert_eq!(e.symbols, vec!["Adom_Sample"]);
assert_eq!(e.footprints, vec!["Adom_Sample"]);
assert_eq!(e.files, 12, "10 footprints + 1 symbol library + icon; metadata.json is not extracted");
assert!(third.join("symbols/com_adom_kicad-bridge_sample-lib/Adom_Sample.kicad_sym").is_file());
assert!(third.join("footprints/com_adom_kicad-bridge_sample-lib/Adom_Sample.pretty/AdomSample_R_0805_2012Metric.kicad_mod").is_file());
assert!(third.join("resources/com_adom_kicad-bridge_sample-lib/icon.png").is_file());
let mut total = 0u64;
for f in walk(&third) {
total += std::fs::metadata(&f).unwrap().len();
}
// install_size counts metadata.json too, which PCM does not extract.
assert!(total < v["install_size"].as_u64().unwrap() && total > v["install_size"].as_u64().unwrap() - 4000, "{total} vs {}", v["install_size"]);
let _ = std::fs::remove_dir_all(&third);
}
}
fn walk(dir: &Path) -> Vec<PathBuf> {
let mut out = Vec::new();
for e in std::fs::read_dir(dir).unwrap().flatten() {
let p = e.path();
if p.is_dir() {
out.extend(walk(&p));
} else {
out.push(p);
}
}
out
}