Index: server/static/rpcexplorer/rpc-descriptor-util.html |
diff --git a/server/static/rpcexplorer/rpc-descriptor-util.html b/server/static/rpcexplorer/rpc-descriptor-util.html |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8b2b753e66730ee6b8343bf09bb9f72abbd44042 |
--- /dev/null |
+++ b/server/static/rpcexplorer/rpc-descriptor-util.html |
@@ -0,0 +1,190 @@ |
+<!-- |
+ ~ // Copyright 2016 The Chromium Authors. All rights reserved. |
+ ~ // Use of this source code is governed by a BSD-style license that can be |
+ ~ // found in the LICENSE file. |
+ ~ |
+ --> |
+ |
+<script> |
+'use strict'; |
+ |
+// rpcExplorer.descUtil exposes helper methods to work with protobuf |
Bons
2016/02/13 17:18:25
You describe some classes in HTML comments and oth
nodir
2016/02/17 02:02:13
Done.
|
+// descriptor messages. |
+// Primarily it implements name and comments resolution in a FileDescriptorSet. |
+ |
+var rpcExplorer = (function(rpcExplorer) { |
+ |
+ rpcExplorer.descUtil = { |
+ |
+ // for all descriptors in the file (FileDescriptorProto message), |
+ // annotate resolves SourceLocationInfo.Location message and assigns it |
+ // to source_code_info property of the descriptor. |
+ // Prerequisite reading: |
+ // https://github.com/luci/luci-go/blob/ea240d0/common/proto/google/descriptor/descriptor.proto#L713 |
+ annotate: function (file) { |
+ if (!file.source_code_info) { |
+ return; |
+ } |
+ |
+ // First, build a map { path -> location message }. |
+ function key(path) { |
+ var key = ''; |
+ for (var i = 0; i < path.length; i++) { |
+ key += path[i] + '.'; |
+ } |
+ return key; |
+ } |
+ var locationMap = {}; |
+ for (var i = 0; i < file.source_code_info.location.length; i++) { |
+ var loc = file.source_code_info.location[i]; |
+ if (loc.path) { |
+ locationMap[key(loc.path)] = loc; |
+ } |
+ } |
+ |
+ // Now join all descriptors in file with the map. |
+ var path = []; |
+ |
+ function annotateList(list, field, fn) { |
+ if (!list) { |
+ return; |
+ } |
+ path.push(field, 0); |
+ for (var i = 0; i < list.length; i++) { |
+ path[path.length - 1] = i; |
+ list[i].source_code_info = locationMap[key(path)]; |
+ if (fn) { |
+ fn(list[i]); |
+ } |
+ } |
+ path.pop(); |
+ path.pop(); |
+ } |
+ |
+ // the magic numbers below are message field numbers defined in |
Bons
2016/02/13 17:18:25
comments should be full sentences with a period.
nodir
2016/02/17 02:02:13
Done.
|
+ // descriptor.proto |
+ |
+ function annotateMessage(msg) { |
+ annotateList(msg.field, 2); |
+ annotateList(msg.nested_type, 3, annotateMessage); |
+ annotateList(msg.enum_type, 4, annotateEnum); |
+ } |
+ |
+ function annotateEnum(e) { |
+ annotateList(e.value, 2); |
+ } |
+ |
+ function annotateService(svc) { |
+ annotateList(svc.method, 2); |
+ } |
+ |
+ annotateList(file.message_type, 4, annotateMessage); |
+ annotateList(file.enum_type, 5, annotateEnum); |
+ annotateList(file.service, 6, annotateService); |
+ }, |
+ |
+ // annotates a FileDescriptorSet. |
+ annotateSet: function (fileSet) { |
+ for (var i = 0; i < fileSet.file.length; i++) { |
+ this.annotate(fileSet.file[i]); |
+ } |
+ }, |
+ |
+ splitFullName: function(fullName) { |
+ var lastDot = fullName.lastIndexOf('.'); |
+ if (lastDot === -1) { |
+ return { |
+ pkg: '', |
+ name: fullName |
+ }; |
+ } |
+ |
+ return { |
+ pkg: fullName.substr(0, lastDot), |
+ name: fullName.substr(lastDot + 1) |
+ }; |
+ }, |
+ |
+ // resolves services, methods, messages and enums by name. |
+ resolve: function(desc, name) { |
+ if (!desc || !name) { |
+ return null; |
+ } |
+ name = this.splitFullName(name); |
+ |
+ var self = this; |
+ |
+ // searches in each list. |
+ function checkLists(lists) { |
+ if (!lists) { |
+ return null; |
+ } |
+ for (var type in lists) { |
+ var desc = self.findByName(lists[type], name.name); |
+ if (desc) { |
+ return {type: type, desc: desc }; |
+ } |
+ } |
+ return null; |
+ } |
+ |
+ // Check top-level descriptors. |
+ for (var i = 0; i < desc.file.length; i++) { |
+ var file = desc.file[i]; |
+ if (file['package'] != name.pkg) { |
+ continue |
+ } |
+ |
+ var result = checkLists({ |
+ service: file.service, |
+ message: file.message_type, |
+ 'enum': file.enum_type |
+ }); |
+ if (result) { |
+ return result; |
+ } |
+ } |
+ |
+ // Recurse. |
+ var parent = this.resolve(desc, name.pkg); |
+ if (!parent) { |
+ return null; |
+ } |
+ switch (parent.type) { |
+ case 'service': |
+ return checkLists({ method: parent.desc.method }); |
+ |
+ case 'message': |
+ return checkLists({ |
+ message: parent.desc.nested_type, |
+ 'enum': parent.desc.enum_type |
Bons
2016/02/13 17:18:26
instead of using a reserved word why not say enumT
nodir
2016/02/17 02:02:13
used enumType and messageType
|
+ }); |
+ |
+ default: |
+ return null; |
+ } |
+ }, |
+ |
+ findByName: function(array, name) { |
+ if (!array) { |
+ return null; |
+ } |
+ for (var i = 0; i < array.length; i++) { |
+ if (array[i].name == name) { |
+ return array[i]; |
+ } |
+ } |
+ return null; |
+ }, |
+ |
+ trimPrefixDot: function(name) { |
+ if (typeof name == 'string' && name.charAt(0) == '.') { |
+ name = name.substr(1); |
+ } |
+ return name; |
+ } |
+ }; |
+ |
+ return rpcExplorer; |
+}(rpcExplorer || {})); |
+</script> |