// Copyright 2021 The Pigweed Authors // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. import {ProtoCollection} from '@pigweed/pw_protobuf_compiler'; import { MethodDescriptorProto, ServiceDescriptorProto, } from 'google-protobuf/google/protobuf/descriptor_pb'; import {hash} from './hash'; interface ChannelOutput { (data: Uint8Array): void; } export class Channel { readonly id: number; private output: ChannelOutput; constructor(id: number, output: ChannelOutput = () => {}) { this.id = id; this.output = output; } send(data: Uint8Array) { this.output(data); } } /** Describes an RPC service. */ export class Service { readonly name: string; readonly id: number; readonly methods = new Map(); readonly methodsByName = new Map(); constructor( descriptor: ServiceDescriptorProto, protoCollection: ProtoCollection, packageName: string ) { this.name = packageName + '.' + descriptor.getName()!; this.id = hash(this.name); descriptor .getMethodList() .forEach((methodDescriptor: MethodDescriptorProto) => { const method = new Method(methodDescriptor, protoCollection, this); this.methods.set(method.id, method); this.methodsByName.set(method.name, method); }); } } export enum MethodType { UNARY, SERVER_STREAMING, CLIENT_STREAMING, BIDIRECTIONAL_STREAMING, } /** Describes an RPC method. */ export class Method { readonly service: Service; readonly name: string; readonly id: number; readonly clientStreaming: boolean; readonly serverStreaming: boolean; readonly requestType: any; readonly responseType: any; constructor( descriptor: MethodDescriptorProto, protoCollection: ProtoCollection, service: Service ) { this.name = descriptor.getName()!; this.id = hash(this.name); this.service = service; this.serverStreaming = descriptor.getServerStreaming()!; this.clientStreaming = descriptor.getClientStreaming()!; const requestTypePath = descriptor.getInputType()!; const responseTypePath = descriptor.getOutputType()!; // Remove leading period if it exists. this.requestType = protoCollection.getMessageCreator( requestTypePath.replace(/^\./, '') )!; this.responseType = protoCollection.getMessageCreator( responseTypePath.replace(/^\./, '') )!; } get type(): MethodType { if (this.clientStreaming && this.serverStreaming) { return MethodType.BIDIRECTIONAL_STREAMING; } else if (this.clientStreaming && !this.serverStreaming) { return MethodType.CLIENT_STREAMING; } else if (!this.clientStreaming && this.serverStreaming) { return MethodType.SERVER_STREAMING; } else if (!this.clientStreaming && !this.serverStreaming) { return MethodType.UNARY; } throw Error('Unhandled streaming condition'); } }