diff --git a/src/field.js b/src/field.js index d69706eed..d203fdf3c 100644 --- a/src/field.js +++ b/src/field.js @@ -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 diff --git a/src/namespace.js b/src/namespace.js index 03f3bdae5..47be33699 100644 --- a/src/namespace.js +++ b/src/namespace.js @@ -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.} 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 === ".") @@ -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); }; /** @@ -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.} 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.} 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_) { diff --git a/tests/api_namespace.js b/tests/api_namespace.js index 8352efef4..85c067ddc 100644 --- a/tests/api_namespace.js +++ b/tests/api_namespace.js @@ -12,7 +12,9 @@ enum Enm {\ ONE = 1;\ TWO = 2;\ }\ -message Msg {}\ +message Msg {\ + message Enm {}\ +}\ service Svc {}"; tape.test("reflected namespaces", function(test) { @@ -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");