import { CanonicalName } from './name-input-types'; import { INTL_NAME_TOKENS } from './tokens'; export const UNIVERSAL_NAME_PATTERN = 'UNIVERSAL' as const; function initial(value?: string) { return (value?.[0] ?? '').toUpperCase(); } function valueOrEmpty(v?: string) { return v ?? ''; } export function formatNameByPattern(pattern: string, ctx: CanonicalName): string { if (pattern === UNIVERSAL_NAME_PATTERN) { return [ctx.firstName, ctx.lastName, ctx.patronymic].filter(Boolean).join(' ').trim(); } const TOKEN_REGEX = /(Pr|Su|FF|LL|PP|MM|F|L|P|M)/g; const resolveToken = (token: string): string => { switch (token) { case INTL_NAME_TOKENS.FIRST.FULL: return valueOrEmpty(ctx.firstName); case INTL_NAME_TOKENS.FIRST.INITIAL: return initial(ctx.firstName); case INTL_NAME_TOKENS.LAST.FULL: return valueOrEmpty(ctx.lastName); case INTL_NAME_TOKENS.LAST.INITIAL: return initial(ctx.lastName); case INTL_NAME_TOKENS.MIDDLE.FULL: return valueOrEmpty(ctx.middleName); case INTL_NAME_TOKENS.MIDDLE.INITIAL: return initial(ctx.middleName); case INTL_NAME_TOKENS.PATRONYMIC.FULL: return valueOrEmpty(ctx.patronymic); case INTL_NAME_TOKENS.PATRONYMIC.INITIAL: return initial(ctx.patronymic); case INTL_NAME_TOKENS.PREFIX: return valueOrEmpty(ctx.prefix); case INTL_NAME_TOKENS.SUFFIX: return valueOrEmpty(ctx.suffix); default: return ''; } }; const renderTokensWithPresence = (str: string): { text: string; hasValue: boolean } => { let hasValue = false; const text = str.replace(TOKEN_REGEX, (token) => { const value = resolveToken(token); if (value !== '') hasValue = true; return value; }); return { text, hasValue }; }; let out = pattern; const blockRe = /\{([^{}]*)\}/g; while (true) { const before = out; out = out.replace(blockRe, (_, inner: string) => { const { text, hasValue } = renderTokensWithPresence(inner); return hasValue ? text : ''; }); if (out === before) break; } out = renderTokensWithPresence(out).text; return out.replace(/\s+/g, ' ').trim(); }