123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- /**
- * Module dependencies
- */
- var _ = require('@sailshq/lodash');
- var TYPES = require('./helpers/types');
- var dehydrate = require('./dehydrate');
- /**
- * Given an exemplar schema and a keypath, return information
- * about the specified segment.
- *
- * If the path is inside of a generic, then the exemplar is '*',
- * and this path is optional. If the path is inside of a `ref`,
- * then the exemplar is '===', and this path is optional.
- *
- *
- * @param {*} schema - an exemplar
- * @param {String} path - a valid dot-deliminited path, where empty string ('') is the root (e.g. "" or "foo" or "foo.bar" or "foo.0.bar.0.baz")
- *
- * @returns {Dictionary}
- * @property {*} exemplar
- * @property {Boolean} optional
- *
- * @throws E_MALFORMED_PATH
- * If keypath is invalid for the provided schema
- *
- * @throws E_UNREACHABLE
- * if keypath is unreachable (meaning it is not allowed in the schema)
- */
- module.exports = function getPathInfo (schema, path) {
- // Dehydrate the schema to avoid circular recursion
- var dehydratedSchema = dehydrate(schema);
- // Derive an array of "hops" from the provided keypath.
- var hops = (path === '') ? [] : path.split('.');
- // These variables are used by the logic below.
- var currentExemplar = dehydratedSchema;
- // By default the path is not optional.
- var optional = false;
- _.each(hops, function dereferenceEach(hop){
- if (_.isArray(currentExemplar)) {
- if (!_.isFinite(+hop)) {
- // If the hop cannot be cast to a positive integer,
- // something funny is going on.
- throw (function (){
- var err = new Error('Malformed keypath: "'+path+'" (non-numeric hop `'+hop+'` is not a valid array index)');
- err.code = 'E_MALFORMED_PATH';
- return err;
- })();
- }
- // generic array
- // (same thing as `['*']`)
- if (_.isEqual(currentExemplar, [])) {
- optional = true;
- currentExemplar = TYPES.json.getExemplar();
- }
- // patterned array
- else {
- optional = false;
- if (_.isUndefined(currentExemplar[0])) {
- throw (function (){
- var err = new Error('Keypath: "'+path+'" is unreachable in schema');
- err.code = 'E_UNREACHABLE';
- return err;
- })();
- }
- else {
- currentExemplar = currentExemplar[0];
- }
- }
- }
- else if (_.isObject(currentExemplar)) {
- // generic dictionary
- if (_.isEqual(currentExemplar, {})) {
- optional = true;
- currentExemplar = TYPES.json.getExemplar();
- }
- // facted dictionary
- else {
- optional = false;
- if (_.isUndefined(currentExemplar[hop])) {
- throw (function (){
- var err = new Error('Keypath: "'+path+'" is unreachable in schema');
- err.code = 'E_UNREACHABLE';
- return err;
- })();
- }
- else {
- currentExemplar = currentExemplar[hop];
- }
- }
- }
- // generic json
- else if (TYPES.json.isExemplar(currentExemplar)) {
- optional = true;
- currentExemplar = TYPES.json.getExemplar();
- }
- // mutable reference
- else if (TYPES.ref.isExemplar(currentExemplar)) {
- optional = true;
- currentExemplar = TYPES.ref.getExemplar();
- }
- // lamda
- else if (TYPES.lamda.isExemplar(currentExemplar)) {
- throw (function (){
- var err = new Error('Keypath: "'+path+'" is unreachable in schema');
- err.code = 'E_UNREACHABLE';
- return err;
- })();
- }
- // primitive
- else {
- throw (function (){
- var err = new Error('Keypath: "'+path+'" is unreachable in schema');
- err.code = 'E_UNREACHABLE';
- return err;
- })();
- }
- });
- return {
- exemplar: currentExemplar,
- optional: optional
- };
- };
|