import { type BicCheck } from "./validation_bic_types.ts";
function invalid(reason: string): BicCheck {
return { valid: false, normalised: null, institution: null, country: null, location: null, branch: null, reason };
}
function isUpper(ch: string): boolean {
return ch >= "A" && ch <= "Z";
}
/**
* Check the format of a BIC and split it into institution, country, location
* and branch.
*
* Well formed is not registered: only SWIFT's directory knows whether the code
* is assigned. Validators answer rather than throw.
*/
export function validateBic(value: string): BicCheck {
if (typeof value !== "string") return invalid("empty");
let bic = "";
for (const ch of value) {
if (ch === " ") continue;
// Folded by hand: toUpperCase() is Unicode-aware and Rust's ASCII fold is not.
bic += ch >= "a" && ch <= "z" ? String.fromCharCode(ch.charCodeAt(0) - 32) : ch;
}
if (bic.length === 0) return invalid("empty");
for (const ch of bic) {
if (!isUpper(ch) && !(ch >= "0" && ch <= "9")) return invalid("bad-character");
}
if (bic.length !== 8 && bic.length !== 11) return invalid("bad-length");
if (!isUpper(bic[4]) || !isUpper(bic[5])) return invalid("bad-country");
return {
valid: true,
normalised: bic,
institution: bic.slice(0, 4),
country: bic.slice(4, 6),
location: bic.slice(6, 8),
// An 8-character BIC is the primary office, which the 11-character form spells XXX.
branch: bic.length === 11 ? bic.slice(8, 11) : "XXX",
reason: null,
};
}