• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2021 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14
15import {ProtoCollection} from '@pigweed/pw_protobuf_compiler';
16import {
17  MethodDescriptorProto,
18  ServiceDescriptorProto,
19} from 'google-protobuf/google/protobuf/descriptor_pb';
20
21import {hash} from './hash';
22
23interface ChannelOutput {
24  (data: Uint8Array): void;
25}
26
27export class Channel {
28  readonly id: number;
29  private output: ChannelOutput;
30
31  constructor(id: number, output: ChannelOutput = () => {}) {
32    this.id = id;
33    this.output = output;
34  }
35
36  send(data: Uint8Array) {
37    this.output(data);
38  }
39}
40
41/** Describes an RPC service. */
42export class Service {
43  readonly name: string;
44  readonly id: number;
45  readonly methods = new Map<number, Method>();
46  readonly methodsByName = new Map<string, Method>();
47
48  constructor(
49    descriptor: ServiceDescriptorProto,
50    protoCollection: ProtoCollection,
51    packageName: string
52  ) {
53    this.name = packageName + '.' + descriptor.getName()!;
54    this.id = hash(this.name);
55    descriptor
56      .getMethodList()
57      .forEach((methodDescriptor: MethodDescriptorProto) => {
58        const method = new Method(methodDescriptor, protoCollection, this);
59        this.methods.set(method.id, method);
60        this.methodsByName.set(method.name, method);
61      });
62  }
63}
64
65export enum MethodType {
66  UNARY,
67  SERVER_STREAMING,
68  CLIENT_STREAMING,
69  BIDIRECTIONAL_STREAMING,
70}
71
72/** Describes an RPC method. */
73export class Method {
74  readonly service: Service;
75  readonly name: string;
76  readonly id: number;
77  readonly clientStreaming: boolean;
78  readonly serverStreaming: boolean;
79  readonly requestType: any;
80  readonly responseType: any;
81
82  constructor(
83    descriptor: MethodDescriptorProto,
84    protoCollection: ProtoCollection,
85    service: Service
86  ) {
87    this.name = descriptor.getName()!;
88    this.id = hash(this.name);
89    this.service = service;
90    this.serverStreaming = descriptor.getServerStreaming()!;
91    this.clientStreaming = descriptor.getClientStreaming()!;
92
93    const requestTypePath = descriptor.getInputType()!;
94    const responseTypePath = descriptor.getOutputType()!;
95
96    // Remove leading period if it exists.
97    this.requestType = protoCollection.getMessageCreator(
98      requestTypePath.replace(/^\./, '')
99    )!;
100    this.responseType = protoCollection.getMessageCreator(
101      responseTypePath.replace(/^\./, '')
102    )!;
103  }
104
105  get type(): MethodType {
106    if (this.clientStreaming && this.serverStreaming) {
107      return MethodType.BIDIRECTIONAL_STREAMING;
108    } else if (this.clientStreaming && !this.serverStreaming) {
109      return MethodType.CLIENT_STREAMING;
110    } else if (!this.clientStreaming && this.serverStreaming) {
111      return MethodType.SERVER_STREAMING;
112    } else if (!this.clientStreaming && !this.serverStreaming) {
113      return MethodType.UNARY;
114    }
115    throw Error('Unhandled streaming condition');
116  }
117}
118