app
Adom Chip Fetcher
Public Made by Adomby adom
Your whole parts library — manufacturer-grade chip CAD (symbol, footprint, 3D) one tap from your EDA tool.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
//! JST (J.S.T. Mfg. Co., Ltd.) — connector CAD source plugin.
//!
//! JST is unusual: its CAD is **email-gated**. Hitting a download link does
//! NOT return the file — it returns a License Agreement form, and on submit
//! (since April 2025) JST EMAILS the CAD as a `.zip` attachment. The full
//! flow (native-browser bridge, form fill, Gmail retrieval) lives in
//! `playbooks/jst.md`. This module is just the URL/part-number plumbing.
//!
//! Two sites:
//! * jst.com (USA) — Cloudflare-walled against pup; spec tables only; it
//! routes to jst-mfg.com for CAD. Drive it with the native-browser
//! bridge, never pup.
//! * jst-mfg.com (global) — where the CAD + catalogs actually live.
//!
//! No account login is required for CAD — only the agreement form + a
//! reachable email address.
/// The master index of every JST series ("Series Alphabet Search"). Each
/// series on this page is an `index.php?series=<id>` link. Start here when
/// you don't already know a part's series id.
pub fn series_index_url() -> &'static str {
"https://www.jst-mfg.com/product/index.php?type=5"
}
/// A series listing page (all parts + their CAD download buttons).
pub fn series_url(series_id: u32) -> String {
format!("https://www.jst-mfg.com/product/index.php?series={series_id}")
}
/// Global-site keyword search — use to resolve an MPN to its series when the
/// series id is unknown.
pub fn search_url(mpn: &str) -> String {
let mut q = String::with_capacity(mpn.len());
for b in mpn.bytes() {
match b {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => q.push(b as char),
_ => q.push_str(&format!("%{:02X}", b)),
}
}
format!("https://www.jst-mfg.com/search/?q={q}")
}
/// JST CAD document kinds. The `doc=` query param selects the format, BUT the
/// numeric mapping is per-series — newer series expose STEP, older ones only
/// IGES. ALWAYS read the column header on the series page; treat these as the
/// common-case defaults (verified on the PH series, series=199), not gospel.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DocKind {
Iges, // doc=1 (common) — 3D, .IGS
Step, // doc=2 (common) — 3D, .STEP ← preferred for chip-fetcher
Pdf3d, // doc=3 (common) — 3D-PDF
Pdf2d, // doc=4 (common) — 2D-PDF drawing
}
impl DocKind {
/// The common-case `doc=` number for this kind. Verify against the page.
pub fn default_doc(&self) -> u32 {
match self {
DocKind::Iges => 1,
DocKind::Step => 2,
DocKind::Pdf3d => 3,
DocKind::Pdf2d => 4,
}
}
}
/// The email-gated download link for one file. GET-ing this in the
/// native browser:
/// 1. sets the PHP session binding for (series, doc, filename), and
/// 2. returns the License Agreement HTML form.
/// You then accept cookies, fill the form, and call the page's `download()`
/// JS — JST emails the `.zip`. `filename` is the part's zip name, e.g.
/// "PHR-2.zip".
pub fn download_url(series_id: u32, doc: u32, filename: &str) -> String {
format!(
"https://www.jst-mfg.com/product/index.php?type=10&series={series_id}&doc={doc}&filename={filename}"
)
}
/// Series catalog/datasheet PDF — a DIRECT download (no gate). `series_slug`
/// is the short uppercase series tag, e.g. "PH" → ePH.pdf. JST has no
/// per-part datasheet; use the catalog + the 2D-PDF drawing.
pub fn catalog_pdf_url(series_slug: &str) -> String {
format!(
"https://www.jst-mfg.com/product/pdf/eng/e{}.pdf",
series_slug.to_ascii_uppercase()
)
}
/// The required License Agreement form fields, in page order. Field ids on
/// the page match these keys. `url` is optional; the rest are mandatory.
pub const FORM_FIELDS: &[&str] = &["company", "section", "name", "address", "tel", "email", "url"];
/// Normalize a JST ordering MPN to its catalog part number for searching:
/// strip the lead-free/plating suffix `(LF)(SN)` and any trailing
/// parenthetical option groups, plus a trailing `T` suction-cap option on
/// SMT headers (per JST + DigiKey TechForum guidance). E.g.
/// "B10B-PH-K-S(LF)(SN)" -> "B10B-PH-K-S"
/// "BM03B-SRSS-TBT(LF)(SN)" -> "BM03B-SRSS-TB"
pub fn base_part(mpn: &str) -> String {
// Drop everything from the first '(' onward (all option groups).
let mut s = match mpn.find('(') {
Some(i) => mpn[..i].to_string(),
None => mpn.to_string(),
};
s = s.trim().to_string();
// Trailing suction-cap "T" on SMT headers (…-TBT -> …-TB). Only strip a
// lone trailing T that follows another uppercase letter, to avoid eating
// a real terminal T that is part of the code.
if s.ends_with('T') {
let chars: Vec<char> = s.chars().collect();
if chars.len() >= 2 && chars[chars.len() - 2].is_ascii_uppercase() {
s.pop();
}
}
s
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn strips_plating_and_option_suffixes() {
assert_eq!(base_part("B10B-PH-K-S(LF)(SN)"), "B10B-PH-K-S");
assert_eq!(base_part("BM03B-SRSS-TBT(LF)(SN)"), "BM03B-SRSS-TB");
assert_eq!(base_part("PHR-2"), "PHR-2");
assert_eq!(base_part("S2B-PH-K-S(LF)(SN)"), "S2B-PH-K-S");
}
#[test]
fn doc_defaults() {
assert_eq!(DocKind::Step.default_doc(), 2);
assert_eq!(DocKind::Iges.default_doc(), 1);
}
#[test]
fn urls() {
assert_eq!(
download_url(199, 2, "PHR-2.zip"),
"https://www.jst-mfg.com/product/index.php?type=10&series=199&doc=2&filename=PHR-2.zip"
);
assert_eq!(catalog_pdf_url("ph"), "https://www.jst-mfg.com/product/pdf/eng/ePH.pdf");
}
}