src/JSONPointer/index.js
import t from 'tcomb';
/**
* An array of Strings
* TokenList holds JSONPath tokens.
* @typedef {Array<string>} TokenList
*/
const TokenList = t.list(t.String);
/**
* JSON Pointer defines a string format for identifying a specific value within a JSON document.
* This class holds methods for manipulation of pointers.
*
* For more information:
* - http://tools.ietf.org/html/rfc6901
* - http://jsonpatch.com/#json-pointer
*/
class JSONPointer {
/**
* JSONPointer constructors
*
* @param {Object} opts - options.
* @param {TokenList} opts.tokens - json path tokens
*/
constructor(opts = {}) {
const tokens = TokenList(opts.tokens || []);
this.tokens = tokens;
Object.freeze(this.tokens);
Object.freeze(this);
}
static ofTokens(tokens) {
return new JSONPointer({ tokens });
}
/**
* parse str and return JSONPointer
*
* If x starts with '/' create will treat x as a JSONPointer.
* All other string will be treated like immutable js pointer (eg.: 'foo.bar')
*
* @param {string} str - pointer string
* @returns {JSONPointer}
*/
static ofString(str) {
if(!t.String.is(str))
throw new Error('JSONPointer::ofString - expected string');
if(str === '') return new JSONPointer({
tokens: []
});
// pointer to key "" (rfc)
if(str === '/') return new JSONPointer({
tokens: ['']
});
if(str.startsWith('/')) return new JSONPointer({
tokens: str.split('/').slice(1)
});
return new JSONPointer({
tokens: str.split('.')
});
}
/**
* get rfc string representation
*
* @returns {string}
*/
toRFC() {
return '/'.concat(this.tokens.join('/'));
}
/**
* get immutable js string representation
*
* @returns {string}
*/
toImmutable() {
return this.tokens.join('.');
}
/**
* get first token
*
* Return value is the first entry from token list.
* @returns {string}
*/
first() {
return this.tokens[ 0 ] || null;
}
/**
* get last token
*
* Return value is the last entry from token list.
* @returns {string}
*/
last() {
return this.tokens[ this.tokens.length - 1 ] || null;
}
/**
* return length of token array
* @returns {number}
*/
size() {
return this.tokens.length;
}
/**
* concat two JSONPointer
*
* @param {JSONPointer} sub - the pointer to concat
* @returns {JSONPointer}
*/
concat(sub) {
if(!sub instanceof JSONPointer)
throw new Error('JSONPointer#concat argument should be JSONPointer');
return new JSONPointer({
tokens: this.tokens.concat(sub.tokens)
});
}
/**
* slice JSONPointer
*
* slice has the same signature like Array.prototype.slice.
*
* @returns {JSONPointer}
*/
slice() {
return new JSONPointer({
tokens: Array.prototype.slice.apply(this.tokens, arguments)
});
}
}
export default JSONPointer;