src/pathTemplate.ts
Methods |
Accessors |
constructor(data: string)
|
||||||||
Defined in src/pathTemplate.ts:64
|
||||||||
Parameters :
|
inspect |
inspect()
|
Defined in src/pathTemplate.ts:169
|
Renders the path template.
Returns :
string
contains const names matched to binding values |
match | ||||||||
match(path: string)
|
||||||||
Defined in src/pathTemplate.ts:82
|
||||||||
Matches a fully-qualified path template string.
Parameters :
Returns :
Bindings
contains const names matched to binding values |
render | ||||||||
render(bindings: Bindings)
|
||||||||
Defined in src/pathTemplate.ts:134
|
||||||||
Renders a path template using the provided bindings.
Parameters :
Returns :
string
a rendered representation of the path template |
size |
getsize()
|
Defined in src/pathTemplate.ts:58
|
segments |
getsegments()
|
Defined in src/pathTemplate.ts:62
|
import has = require('lodash.has');
import * as util from 'util';
import * as extras from './parserExtras';
const parser = require('./pathTemplateParser');
export interface ParseResult {
size: number;
segments: Segment[];
}
export interface Segment {
kind: number;
literal: string;
}
export interface Bindings {
[index: string]: string;
}
export class PathTemplate {
private readonly parseResult: ParseResult;
get size(): number {
return this.parseResult.size;
}
get segments(): Segment[] {
return this.parseResult.segments;
}
/**
* @param {String} data the of the template
*
* @constructor
*/
constructor(data: string) {
this.parseResult = extras.finishParse(parser.parse(data));
}
/**
* Matches a fully-qualified path template string.
*
* @param {String} path a fully-qualified path template string
* @return {Object} contains const names matched to binding values
* @throws {TypeError} if path can't be matched to this template
*/
match(path: string): Bindings {
const pathSegments = path.split('/');
const bindings: Bindings = {};
let segmentCount = this.size;
let current: string;
let index = 0;
this.segments.forEach(segment => {
if (index > pathSegments.length) {
return;
}
if (segment.kind === extras.BINDING) {
current = segment.literal;
} else if (segment.kind === extras.TERMINAL) {
if (segment.literal === '*') {
bindings[current] = pathSegments[index];
index += 1;
} else if (segment.literal === '**') {
const size = pathSegments.length - segmentCount + 1;
segmentCount += size - 1;
bindings[current] = pathSegments.slice(index, index + size).join('/');
index += size;
} else if (segment.literal === pathSegments[index]) {
index += 1;
} else {
const msg = util.format(
"mismatched literal (index=%d): '%s' != '%s'",
index,
segment.literal,
pathSegments[index]
);
throw new TypeError(msg);
}
}
});
if (index !== pathSegments.length || index !== segmentCount) {
const msg = util.format(
'match error: could not instantiate a path template from %s',
path
);
throw new TypeError(msg);
}
return bindings;
}
/**
* Renders a path template using the provided bindings.
*
* @param {Object} bindings a mapping of const names to binding strings
* @return {String} a rendered representation of the path template
* @throws {TypeError} if a key is missing, or if a sub-template cannot be
* parsed
*/
render(bindings: Bindings): string {
const out: Segment[] = [];
let inABinding = false;
this.segments.forEach(segment => {
if (segment.kind === extras.BINDING) {
if (!has(bindings, segment.literal)) {
const msg = util.format(
'Value for key %s is not provided in %s',
segment.literal,
bindings
);
throw new TypeError(msg);
}
const tmp = new PathTemplate(bindings[segment.literal]);
Array.prototype.push.apply(out, tmp.segments);
inABinding = true;
} else if (segment.kind === extras.END_BINDING) {
inABinding = false;
} else if (inABinding) {
return;
} else {
out.push(segment);
}
});
const result = formatSegments(out);
this.match(result);
return result;
}
/**
* Renders the path template.
*
* @return {string} contains const names matched to binding values
*/
inspect() {
return formatSegments(this.segments);
}
}
/**
* Creates the string representattion for the segments.
* @param {Object[]} segments - The array of segments.
* @return {string} - A string representing segments in the path template
* format.
*/
function formatSegments(segments: Segment[]): string {
let out = '';
let slash = true;
segments.forEach(segment => {
if (segment.kind === extras.TERMINAL) {
if (slash) {
out += '/';
}
out += segment.literal;
return;
}
slash = true;
if (segment.kind === extras.BINDING) {
out += '/{' + segment.literal + '=';
slash = false;
} else {
out += segment.literal + '}';
}
});
return out.substring(1);
}