import { trimDot, withDot, newLinesIntoSpaces, splitByNewLine, UNIX_NEW_LINE_CHAR, } from '../shared/utils_generic'; import { ParsedTypings } from '../shared/types_domain_specific'; import { ParserTypings, ParsingRecordDataType } from './types_parser'; /** * @perf use collections, benchmark * @note we do not validate invalid quotes (missing close) */ export const splitArgs = (input: string, sep?: RegExp | string): string[] => { const separator = sep || /\s/g; let isSingleQuoteOpen = false; let isDoubleQuoteOpen = false; let tokenBuffer = []; const tokensFromArgs = []; const characters = input.split(''); for (const element of characters) { const matches = element.match(separator); if (element === "'" && !isDoubleQuoteOpen) { tokenBuffer.push(element); isSingleQuoteOpen = !isSingleQuoteOpen; continue; } else if (element === '"' && !isSingleQuoteOpen) { tokenBuffer.push(element); isDoubleQuoteOpen = !isDoubleQuoteOpen; continue; } if (!isSingleQuoteOpen && !isDoubleQuoteOpen && matches) { if (tokenBuffer.length > 0) { tokensFromArgs.push(tokenBuffer.join('')); tokenBuffer = []; } else if (!!sep) { tokensFromArgs.push(element); } } else { tokenBuffer.push(element); } } if (tokenBuffer.length > 0) { tokensFromArgs.push(tokenBuffer.join('')); } else if (!!sep) { tokensFromArgs.push(''); } return tokensFromArgs; }; /** @note not checking invalid format of nested parens */ export const flattenStartOfAuthority = (input: string): string => { let isParenOpen = false; let isInSoa = false; let soa = ''; let flattened = ''; const addSoa = () => { flattened += newLinesIntoSpaces(soa).replace(/\s+/gm, ' ') + UNIX_NEW_LINE_CHAR; soa = ''; isInSoa = false; }; for (let index = 0; index < input.length; ++index) { const element = input[index]; if ( element === 'S' && input[index + 1] === 'O' && input[index + 2] === 'A' ) { isInSoa = true; soa = ''; } if (!isInSoa) { flattened += element; } else { if (element === '(' && !isParenOpen) { isParenOpen = true; } else if (element === ')' && isParenOpen) { addSoa(); isParenOpen = false; } else if (element === UNIX_NEW_LINE_CHAR && !isParenOpen) { addSoa(); } else { soa += element; } } } return flattened; }; export const removeComments = (text: string): string => { const matchComment = /\\"|"(?:\\"|[^"])*"|((^|[^\\])\;.*)/g; return text.replace(matchComment, (match: string, g1: unknown) => { // if g1 is not set/matched, re-insert it, else remove return !g1 ? match : ''; }); }; export const transformInputText = (text: string): [string, string[]] => { const withoutComments = removeComments(text); const flattened = flattenStartOfAuthority(withoutComments); const lines = splitByNewLine(flattened).filter(x => x.trim() !== ''); return [flattened, lines]; }; export type ZipMapperFnType = ( value: T & Omit, parser: ParserTypings.Parser ) => T; export type ZipObjFnType = ( x: ParsingRecordDataType, parser: ParserTypings.Parser ) => T; export const zipArrayToObj = ( template: string, mapper?: ZipMapperFnType ): ZipObjFnType => (recordData, parser) => { const { tokens, ...rest } = recordData; const obj = { ...rest } as any; template.split(' ').forEach((key, index) => { obj[key] = tokens[index]; }); return (mapper ? mapper(obj as any, parser) : obj) as T; }; export const getRecordNameUsingOrigin = ( name: string, zoneObj: ParsedTypings.Obj ): string => { const $origin = zoneObj.$ORIGIN; if ( !name || name === '@' || name === $origin || trimDot(name) === trimDot($origin) ) { return withDot($origin); } else if (!name.includes($origin) && name.endsWith('.')) { return name + $origin; } else if (!name.includes($origin)) { return name + '.' + $origin; } else { return name; } };