@konfirm/iso7064
Version:
ISO/IEC 7064:2003 - Check character systems
203 lines (172 loc) • 3.87 kB
text/typescript
import type { Alphabet } from '@konfirm/alphabet';
export type ISO7064Options = {
algorithm: string;
designation: number;
radix: number;
modulus: number;
double: boolean;
indices: Alphabet;
alphabet: Alphabet;
};
const storage: WeakMap<ISO7064, Partial<ISO7064Options>> = new WeakMap();
/**
* Implement the common ISO 7064 implementation mechanics
*
* @class ISO7064
*/
export abstract class ISO7064 {
constructor(options: Partial<ISO7064Options> = {}) {
storage.set(this, options);
}
/**
* The algorithm name
*
* @readonly
* @memberof ISO7064
*/
get algorithm(): string {
const { algorithm } = storage.get(this) as ISO7064Options;
return algorithm || 'Custom';
}
/**
* The specification name
*
* @readonly
* @memberof ISO7064
*/
get specification(): string {
const { algorithm } = this;
return `ISO 7064, ${algorithm}`;
}
/**
* The designation (always 0, except for the Modulus implementations)
*
* @readonly
* @memberof ISO7064
*/
get designation(): number {
const { designation } = storage.get(this) as ISO7064Options;
return designation || 0;
}
/**
* The alphabet of allowed characters and from which to obtain the indices
*
* @readonly
* @memberof ISO7064
*/
get indices(): Alphabet {
const { indices, alphabet } = storage.get(this) as ISO7064Options;
return indices || alphabet;
}
/**
* The checksum alphabet
*
* @readonly
* @memberof ISO7064
*/
get alphabet(): Alphabet {
const { alphabet } = storage.get(this) as ISO7064Options;
return alphabet;
}
/**
* The modulus
*
* @readonly
* @memberof ISO7064
*/
get modulus(): number {
const { modulus, alphabet } = storage.get(this) as ISO7064Options;
return modulus || (alphabet && alphabet.length);
}
/**
* The radix
*
* @readonly
* @memberof ISO7064
*/
get radix(): number {
const { radix } = storage.get(this) as ISO7064Options;
return radix;
}
/**
* Does the checksum consist of double digits
*
* @readonly
* @memberof ISO7064
*/
get double(): boolean {
const { double } = storage.get(this) as ISO7064Options;
return double || false;
}
/**
* Normalize input, removing any character not allowed in the input
*
* @param {string} input
* @returns {string} normalized
* @memberof ISO7064
*/
normalize(input: string): string {
const { indices = 'a-zA-Z0-9' } = this;
const purge = new RegExp(`[^${indices}]+`, 'g');
return String(input)
.toUpperCase()
.replace(purge, '');
}
/**
* Calculate the checksum for input
*
* @param {string} input
* @returns {string} checksum
* @memberof ISO7064
*/
checksum(input: string): string {
throw new Error('Checksum method not implemented');
}
/**
* Validate the input
*
* @param {string} input
* @returns {boolean} valid
* @memberof ISO7064
*/
validate(input: string): boolean {
const { indices, alphabet, double } = this;
const pattern = new RegExp(`([${indices}]+)([${alphabet}]{${Number(double) + 1}})`);
const match = this.normalize(input).match(pattern);
if (match) {
const [, num, cc] = match;
return this.checksum(num) === cc;
}
return false;
}
/**
* Generate the normalized output including the checksum
*
* @param {string} input
* @returns {string} generated
* @memberof ISO7064
*/
generate(input: string) {
const normal = this.normalize(input);
return `${normal}${this.checksum(input)}`;
}
/**
* Create a new instance based on the current settings with optional overrides
*
* @param {object} options
* @returns
* @memberof ISO7064
*/
factory(options = {}) {
const { indices, alphabet, modulus, radix, double } = this;
const { constructor: Ctor } = Object.getPrototypeOf(this);
return new Ctor({
indices,
alphabet,
modulus,
radix,
double,
...options
});
}
}