UNPKG

uritemplate

Version:

An UriTemplate implementation of rfc 6570

175 lines (164 loc) 7.82 kB
/*jshint unused:false */ /*global pctEncoder, operators, charHelper, rfcCharHelper, LiteralExpression, UriTemplate, VariableExpression, UriTemplateError*/ var parse = (function () { "use strict"; function parseExpression (expressionText) { var operator, varspecs = [], varspec = null, varnameStart = null, maxLengthStart = null, index, chr = ''; function closeVarname () { var varname = expressionText.substring(varnameStart, index); if (varname.length === 0) { throw new UriTemplateError({expressionText: expressionText, message: "a varname must be specified", position: index}); } varspec = {varname: varname, exploded: false, maxLength: null}; varnameStart = null; } function closeMaxLength () { if (maxLengthStart === index) { throw new UriTemplateError({expressionText: expressionText, message: "after a ':' you have to specify the length", position: index}); } varspec.maxLength = parseInt(expressionText.substring(maxLengthStart, index), 10); maxLengthStart = null; } operator = (function (operatorText) { var op = operators.valueOf(operatorText); if (op === null) { throw new UriTemplateError({expressionText: expressionText, message: "illegal use of reserved operator", position: index, operator: operatorText}); } return op; }(expressionText.charAt(0))); index = operator.symbol.length; varnameStart = index; for (; index < expressionText.length; index += chr.length) { chr = pctEncoder.pctCharAt(expressionText, index); if (varnameStart !== null) { // the spec says: varname = varchar *( ["."] varchar ) // so a dot is allowed except for the first char if (chr === '.') { if (varnameStart === index) { throw new UriTemplateError({expressionText: expressionText, message: "a varname MUST NOT start with a dot", position: index}); } continue; } if (rfcCharHelper.isVarchar(chr)) { continue; } closeVarname(); } if (maxLengthStart !== null) { if (index === maxLengthStart && chr === '0') { throw new UriTemplateError({expressionText: expressionText, message: "A :prefix must not start with digit 0", position: index}); } if (charHelper.isDigit(chr)) { if (index - maxLengthStart >= 4) { throw new UriTemplateError({expressionText: expressionText, message: "A :prefix must have max 4 digits", position: index}); } continue; } closeMaxLength(); } if (chr === ':') { if (varspec.maxLength !== null) { throw new UriTemplateError({expressionText: expressionText, message: "only one :maxLength is allowed per varspec", position: index}); } if (varspec.exploded) { throw new UriTemplateError({expressionText: expressionText, message: "an exploeded varspec MUST NOT be varspeced", position: index}); } maxLengthStart = index + 1; continue; } if (chr === '*') { if (varspec === null) { throw new UriTemplateError({expressionText: expressionText, message: "exploded without varspec", position: index}); } if (varspec.exploded) { throw new UriTemplateError({expressionText: expressionText, message: "exploded twice", position: index}); } if (varspec.maxLength) { throw new UriTemplateError({expressionText: expressionText, message: "an explode (*) MUST NOT follow to a prefix", position: index}); } varspec.exploded = true; continue; } // the only legal character now is the comma if (chr === ',') { varspecs.push(varspec); varspec = null; varnameStart = index + 1; continue; } throw new UriTemplateError({expressionText: expressionText, message: "illegal character", character: chr, position: index}); } // for chr if (varnameStart !== null) { closeVarname(); } if (maxLengthStart !== null) { closeMaxLength(); } varspecs.push(varspec); return new VariableExpression(expressionText, operator, varspecs); } function parse (uriTemplateText) { // assert filled string var index, chr, expressions = [], braceOpenIndex = null, literalStart = 0; for (index = 0; index < uriTemplateText.length; index += 1) { chr = uriTemplateText.charAt(index); if (literalStart !== null) { if (chr === '}') { throw new UriTemplateError({templateText: uriTemplateText, message: "unopened brace closed", position: index}); } if (chr === '{') { if (literalStart < index) { expressions.push(new LiteralExpression(uriTemplateText.substring(literalStart, index))); } literalStart = null; braceOpenIndex = index; } continue; } if (braceOpenIndex !== null) { // here just { is forbidden if (chr === '{') { throw new UriTemplateError({templateText: uriTemplateText, message: "brace already opened", position: index}); } if (chr === '}') { if (braceOpenIndex + 1 === index) { throw new UriTemplateError({templateText: uriTemplateText, message: "empty braces", position: braceOpenIndex}); } try { expressions.push(parseExpression(uriTemplateText.substring(braceOpenIndex + 1, index))); } catch (error) { if (error.prototype === UriTemplateError.prototype) { throw new UriTemplateError({templateText: uriTemplateText, message: error.options.message, position: braceOpenIndex + error.options.position, details: error.options}); } throw error; } braceOpenIndex = null; literalStart = index + 1; } continue; } throw new Error('reached unreachable code'); } if (braceOpenIndex !== null) { throw new UriTemplateError({templateText: uriTemplateText, message: "unclosed brace", position: braceOpenIndex}); } if (literalStart < uriTemplateText.length) { expressions.push(new LiteralExpression(uriTemplateText.substr(literalStart))); } return new UriTemplate(uriTemplateText, expressions); } return parse; }());