Skip to content

Commit

Permalink
Added functionality to filter for multiple types at once in lookup(),…
Browse files Browse the repository at this point in the history
… used by lookupTypeOrEnum(), fixes #740
  • Loading branch information
dcodeIO committed Apr 2, 2017
1 parent fef924e commit d1e3122
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 31 deletions.
8 changes: 3 additions & 5 deletions src/field.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,13 +245,11 @@ Field.prototype.resolve = function resolve() {
if (!Type)
Type = require("./type");

var scope = this.declaringField ? this.declaringField.parent : this.parent;
if (this.resolvedType = scope.lookup(this.type, Type))
this.resolvedType = (this.declaringField ? this.declaringField.parent : this.parent).lookupTypeOrEnum(this.type);
if (this.resolvedType instanceof Type)
this.typeDefault = null;
else if (this.resolvedType = scope.lookup(this.type, Enum))
else // instanceof Enum
this.typeDefault = this.resolvedType.values[Object.keys(this.resolvedType.values)[0]]; // first defined
else
throw Error("unresolvable field type: " + this.type + " in " + scope);
}

// use explicitly set default value if present
Expand Down
65 changes: 40 additions & 25 deletions src/namespace.js
Original file line number Diff line number Diff line change
Expand Up @@ -277,17 +277,18 @@ Namespace.prototype.resolveAll = function resolveAll() {
/**
* Looks up the reflection object at the specified path, relative to this namespace.
* @param {string|string[]} path Path to look up
* @param {function(new: ReflectionObject)} filterType Filter type, one of `protobuf.Type`, `protobuf.Enum`, `protobuf.Service` etc.
* @param {function(new: ReflectionObject)|Array.<function(new: ReflectionObject)>} filterTypes Filter types, any combination of `protobuf.Type`, `protobuf.Enum`, `protobuf.Service` etc.
* @param {boolean} [parentAlreadyChecked=false] If known, whether the parent has already been checked
* @returns {?ReflectionObject} Looked up object or `null` if none could be found
*/
Namespace.prototype.lookup = function lookup(path, filterType, parentAlreadyChecked) {
Namespace.prototype.lookup = function lookup(path, filterTypes, parentAlreadyChecked) {

/* istanbul ignore if */
if (typeof filterType === "boolean") {
parentAlreadyChecked = filterType;
filterType = undefined;
}
if (typeof filterTypes === "boolean") {
parentAlreadyChecked = filterTypes;
filterTypes = undefined;
} else if (filterTypes && !Array.isArray(filterTypes))
filterTypes = [ filterTypes ];

if (util.isString(path) && path.length) {
if (path === ".")
Expand All @@ -298,20 +299,20 @@ Namespace.prototype.lookup = function lookup(path, filterType, parentAlreadyChec

// Start at root if path is absolute
if (path[0] === "")
return this.root.lookup(path.slice(1), filterType);
return this.root.lookup(path.slice(1), filterTypes);
// Test if the first part matches any nested object, and if so, traverse if path contains more
var found = this.get(path[0]);
if (found) {
if (path.length === 1) {
if (!filterType || found instanceof filterType)
if (!filterTypes || filterTypes.indexOf(found.constructor) > -1)
return found;
} else if (found instanceof Namespace && (found = found.lookup(path.slice(1), filterType, true)))
} else if (found instanceof Namespace && (found = found.lookup(path.slice(1), filterTypes, true)))
return found;
}
// If there hasn't been a match, try again at the parent
if (this.parent === null || parentAlreadyChecked)
return null;
return this.parent.lookup(path, filterType);
return this.parent.lookup(path, filterTypes);
};

/**
Expand All @@ -333,38 +334,52 @@ Namespace.prototype.lookup = function lookup(path, filterType, parentAlreadyChec
* @throws {Error} If `path` does not point to a type
*/
Namespace.prototype.lookupType = function lookupType(path) {
var found = this.lookup(path, Type);
var found = this.lookup(path, [ Type ]);
if (!found)
throw Error("no such type");
return found;
};

/**
* Looks up the {@link Service|service} at the specified path, relative to this namespace.
* Looks up the values of the {@link Enum|enum} at the specified path, relative to this namespace.
* Besides its signature, this methods differs from {@link Namespace#lookup|lookup} in that it returns the enum's values directly and throws instead of returning `null`.
* @param {string|string[]} path Path to look up
* @returns {Object.<string,number>} Enum values
* @throws {Error} If `path` does not point to an enum
*/
Namespace.prototype.lookupEnum = function lookupEnum(path) {
var found = this.lookup(path, [ Enum ]);
if (!found)
throw Error("no such enum");
return found.values;
};

/**
* Looks up the {@link Type|type} or {@link Enum|enum} at the specified path, relative to this namespace.
* Besides its signature, this methods differs from {@link Namespace#lookup|lookup} in that it throws instead of returning `null`.
* @param {string|string[]} path Path to look up
* @returns {Service} Looked up service
* @throws {Error} If `path` does not point to a service
* @returns {Type} Looked up type or enum
* @throws {Error} If `path` does not point to a type or enum
*/
Namespace.prototype.lookupService = function lookupService(path) {
var found = this.lookup(path, Service);
Namespace.prototype.lookupTypeOrEnum = function lookupTypeOrEnum(path) {
var found = this.lookup(path, [ Type, Enum ]);
if (!found)
throw Error("no such service");
throw Error("no such type or enum");
return found;
};

/**
* Looks up the values of the {@link Enum|enum} at the specified path, relative to this namespace.
* Besides its signature, this methods differs from {@link Namespace#lookup|lookup} in that it returns the enum's values directly and throws instead of returning `null`.
* Looks up the {@link Service|service} at the specified path, relative to this namespace.
* Besides its signature, this methods differs from {@link Namespace#lookup|lookup} in that it throws instead of returning `null`.
* @param {string|string[]} path Path to look up
* @returns {Object.<string,number>} Enum values
* @throws {Error} If `path` does not point to an enum
* @returns {Service} Looked up service
* @throws {Error} If `path` does not point to a service
*/
Namespace.prototype.lookupEnum = function lookupEnum(path) {
var found = this.lookup(path, Enum);
Namespace.prototype.lookupService = function lookupService(path) {
var found = this.lookup(path, [ Service ]);
if (!found)
throw Error("no such enum");
return found.values;
throw Error("no such service");
return found;
};

Namespace._configure = function(Type_, Service_) {
Expand Down
6 changes: 5 additions & 1 deletion tests/api_namespace.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ enum Enm {\
ONE = 1;\
TWO = 2;\
}\
message Msg {}\
message Msg {\
message Enm {}\
}\
service Svc {}";

tape.test("reflected namespaces", function(test) {
Expand All @@ -34,6 +36,8 @@ tape.test("reflected namespaces", function(test) {

test.ok(ns.lookupType("Msg"), "should lookup types");

test.equal(ns.get("Msg").lookupTypeOrEnum("Enm"), ns.lookup(".ns.Msg.Enm"), "should lookup the nearest type or enum");

test.throws(function() {
ns.lookupType("Enm");
}, Error, "should throw when looking up an enum as a type");
Expand Down

0 comments on commit d1e3122

Please sign in to comment.