UNPKG

@focuson/lens

Version:

A simple implementation of lens using type script

187 lines (186 loc) 7.63 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.replaceTextUsingPathForS = exports.replaceTextFn = exports.processPath = exports.stateCodeBuilder = exports.stateCodeInitials = exports.lensBuilder = exports.prefixNameAndLens = exports.parsePath = exports.tokenisePath = void 0; const index_1 = require("../index"); function makeError(s, msg) { return Error(`${s.errorPrefix} ${msg}`); } function tokenisePath(p) { var i = 0; const tokens = []; var accStart = 0; function addAcc() { let s = p.slice(accStart, i - 1); if (inSpecialBrackets) s = s.startsWith('$') ? s : '$' + s; if (s !== '') tokens.push(s); accStart = i; inSpecialBrackets = false; } var inSpecialBrackets = false; while (true) { if (i >= p.length) { if (accStart < p.length) tokens.push(p.slice(accStart)); return tokens; } const ch = p[i]; i = i + 1; if (inSpecialBrackets && ch === ']') addAcc(); else if (ch === '[' && p[i] && (p[i] === '$' || p[i].match(/[0-9]/))) { addAcc(); inSpecialBrackets = true; } else if (ch === '[' || ch === ']' || ch === '~' || ch === '/') { addAcc(); tokens.push(ch); } else if (!ch.match(/[0-9A-Za-z._$#]/)) throw new Error(`Illegal character [${ch}] at position ${i - 1} in [${p}]`); } } exports.tokenisePath = tokenisePath; function parsePath(path, p) { const tokens = tokenisePath(path); const build = undefined; const s = { build, tokens: tokens.reverse(), errorPrefix: `Error parsing '${path}'.` }; processPath(s, p, false); return s.build; } exports.parsePath = parsePath; function prefixNameAndLens(...choices) { const result = { '/': index_1.Lenses.identity() }; choices.forEach(([p, l]) => result[p] = l); return result; } exports.prefixNameAndLens = prefixNameAndLens; function lensBuilder(prefixs, variables) { const id = index_1.Lenses.identity(); return { initialVariable(name) { var _a; return (_a = variables[name]) === null || _a === void 0 ? void 0 : _a.call(variables, id); }, isVariable(name) { return variables[name] !== undefined; }, foldVariable(acc, name) { var _a; return acc.chain((_a = variables[name]) === null || _a === void 0 ? void 0 : _a.call(variables, id)); }, zero(initial) { return prefixs[initial]; }, foldBracketsPath(acc, path) { return acc.chainCalc(path); }, foldAppend(acc) { return acc.chain(index_1.Lenses.append()); }, foldKey(acc, key) { return acc.focusQuery(key); }, foldLast(acc) { return acc.chain(index_1.Lenses.last()); }, foldNth(acc, n) { return acc.chain(index_1.Lenses.nth(n)); } }; } exports.lensBuilder = lensBuilder; function stateCodeInitials(stateName) { return { '': 'state', '~': 'fullState', '/': `state.copyWithIdentity()` }; } exports.stateCodeInitials = stateCodeInitials; function stateCodeBuilder(initials, optionalsName, focusQuery) { const realFocusQuery = focusQuery ? focusQuery : 'focusQuery'; return { initialVariable(name) { return `${initials['']}.copyWithLens(${optionalsName}.${name}(identityL))`; }, isVariable(name) { return !name.match(/^[0-9]+$/); }, foldVariable(acc, name) { return `.chain(${optionalsName}.${name}(identityL))`; }, zero(initial) { return initials[initial]; }, foldBracketsPath(acc, path) { return acc + `.chainNthFromPath(${path})`; }, foldKey(acc, key) { return acc + `.${realFocusQuery}('${key}')`; }, foldAppend(acc) { return acc + ".chain(Lenses.append())"; }, foldLast(acc) { return acc + ".chain(Lenses.last())"; }, foldNth(acc, n) { return acc + `.chain(Lenses.nth(${n}))`; } }; } exports.stateCodeBuilder = stateCodeBuilder; function processInitialToken(s, p) { const initial = s.tokens[s.tokens.length - 1]; if (initial.startsWith('#')) { s.build = p.initialVariable(initial.slice(1)); if (s.build === undefined) throw makeError(s, `Cannot find variable ${initial}`); s.tokens.pop(); return; } let hasSpecificInitial = initial === '/' || initial === '~'; const actualInitial = hasSpecificInitial ? initial : ''; if (hasSpecificInitial) s.tokens.pop(); s.build = p.zero(actualInitial); if (s.build === undefined) throw makeError(s, `Cannot find initial '${actualInitial}'`); } function processPath(s, p, expectBracket) { if (s.tokens.length == 0) { s.build = p.zero(''); return; } processInitialToken(s, p); while (true) { const token = s.tokens.pop(); if (token === undefined) if (expectBracket) throw makeError(s, 'Ran out of tokens!'); else return; if (token === ']') if (expectBracket) return; else throw makeError(s, 'Unexpected ]'); if (token.startsWith('#')) { let body = token.slice(1); if (p.isVariable(body)) s.build = p.foldVariable(s.build, body); else throw makeError(s, `Variable ${token} not found`); } else if (token === '$append') s.build = p.foldAppend(s.build); else if (token === '$last') s.build = p.foldLast(s.build); else if (token.startsWith('$')) { let body = token.slice(1); let n = Number.parseInt(body); if (n === Number.NaN) throw makeError(s, `'${body} is not a number`); s.build = p.foldNth(s.build, n); } else if (token === '/') { } // else if (token === '~') { throw makeError(s, 'Unexpected ~'); } // else if (token === '[') { const newState = Object.assign({}, s); //note: not copying tokens: this is mutable .. we still consume tokens inside this, but there is a new state. processPath(newState, p, true); //the ] has been popped. s.build = p.foldBracketsPath(s.build, newState.build); } else { s.build = p.foldKey(s.build, token); } } } exports.processPath = processPath; function replaceTextFn(errorPrefix, s, from, f) { function escape(s) { return s.replace(/</g, '&lt;').replace(/>/g, '&gt;'); } const parts = f.slice(1, -1).split("|"); const value = from(parts[0]).getOption(s); if (value === undefined) return ''; if (parts.length == 1) return escape(`${value}`); if (typeof value == 'boolean') { if (parts.length == 3) return escape(value ? parts[2] : parts[1]); throw new Error(`${errorPrefix} Replacing string ${f} and it's a boolean [${value}], but there are ${parts.length - 1} options, not 2`); } if (typeof value == 'number') { if (value >= 0 && value <= parts.length) return escape(parts[value + 1]); throw new Error(`${errorPrefix} Replacing string ${f} and it's a string [${value}], but there are ${parts.length - 1} options, and this value is out of range`); } } exports.replaceTextFn = replaceTextFn; function replaceTextUsingPathForS(errorPrefix, s, fromPath, string) { return string.replace(/{[^}]*}/g, str => replaceTextFn(errorPrefix, s, fromPath, str)); } exports.replaceTextUsingPathForS = replaceTextUsingPathForS;