Functional Weave
Code in TypeScript

validation.bic@1.0.0

impl/typescript.ts

1,529 bytes · the TypeScript implementation · view raw

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,
  };
}