xref: /aosp_15_r20/external/pigweed/pw_rpc/ts/descriptors.ts (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker// Copyright 2021 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker//
3*61c4878aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker// use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker// the License at
6*61c4878aSAndroid Build Coastguard Worker//
7*61c4878aSAndroid Build Coastguard Worker//     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker//
9*61c4878aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker// License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker// the License.
14*61c4878aSAndroid Build Coastguard Worker
15*61c4878aSAndroid Build Coastguard Workerimport {
16*61c4878aSAndroid Build Coastguard Worker  MessageCreator,
17*61c4878aSAndroid Build Coastguard Worker  ProtoCollection,
18*61c4878aSAndroid Build Coastguard Worker} from 'pigweedjs/pw_protobuf_compiler';
19*61c4878aSAndroid Build Coastguard Workerimport { Message } from 'google-protobuf';
20*61c4878aSAndroid Build Coastguard Workerimport {
21*61c4878aSAndroid Build Coastguard Worker  MethodDescriptorProto,
22*61c4878aSAndroid Build Coastguard Worker  ServiceDescriptorProto,
23*61c4878aSAndroid Build Coastguard Worker} from 'google-protobuf/google/protobuf/descriptor_pb';
24*61c4878aSAndroid Build Coastguard Worker
25*61c4878aSAndroid Build Coastguard Workerimport { hash } from './hash';
26*61c4878aSAndroid Build Coastguard Worker
27*61c4878aSAndroid Build Coastguard Workerinterface ChannelOutput {
28*61c4878aSAndroid Build Coastguard Worker  (data: Uint8Array): void;
29*61c4878aSAndroid Build Coastguard Worker}
30*61c4878aSAndroid Build Coastguard Worker
31*61c4878aSAndroid Build Coastguard Workerexport class Channel {
32*61c4878aSAndroid Build Coastguard Worker  readonly id: number;
33*61c4878aSAndroid Build Coastguard Worker  private output: ChannelOutput;
34*61c4878aSAndroid Build Coastguard Worker
35*61c4878aSAndroid Build Coastguard Worker  constructor(
36*61c4878aSAndroid Build Coastguard Worker    id: number,
37*61c4878aSAndroid Build Coastguard Worker    output: ChannelOutput = () => {
38*61c4878aSAndroid Build Coastguard Worker      /* do nothing. */
39*61c4878aSAndroid Build Coastguard Worker    },
40*61c4878aSAndroid Build Coastguard Worker  ) {
41*61c4878aSAndroid Build Coastguard Worker    this.id = id;
42*61c4878aSAndroid Build Coastguard Worker    this.output = output;
43*61c4878aSAndroid Build Coastguard Worker  }
44*61c4878aSAndroid Build Coastguard Worker
45*61c4878aSAndroid Build Coastguard Worker  send(data: Uint8Array) {
46*61c4878aSAndroid Build Coastguard Worker    this.output(data);
47*61c4878aSAndroid Build Coastguard Worker  }
48*61c4878aSAndroid Build Coastguard Worker}
49*61c4878aSAndroid Build Coastguard Worker
50*61c4878aSAndroid Build Coastguard Worker/** Describes an RPC service. */
51*61c4878aSAndroid Build Coastguard Workerexport class Service {
52*61c4878aSAndroid Build Coastguard Worker  readonly name: string;
53*61c4878aSAndroid Build Coastguard Worker  readonly id: number;
54*61c4878aSAndroid Build Coastguard Worker  readonly methods = new Map<number, Method>();
55*61c4878aSAndroid Build Coastguard Worker  readonly methodsByName = new Map<string, Method>();
56*61c4878aSAndroid Build Coastguard Worker
57*61c4878aSAndroid Build Coastguard Worker  constructor(serviceName: string, methodsList: RPCMethodDescriptor[]) {
58*61c4878aSAndroid Build Coastguard Worker    this.name = serviceName;
59*61c4878aSAndroid Build Coastguard Worker    this.id = hash(this.name);
60*61c4878aSAndroid Build Coastguard Worker    methodsList.forEach((methodDescriptor) => {
61*61c4878aSAndroid Build Coastguard Worker      const method = new Method(this, methodDescriptor);
62*61c4878aSAndroid Build Coastguard Worker      this.methods.set(method.id, method);
63*61c4878aSAndroid Build Coastguard Worker      this.methodsByName.set(method.name, method);
64*61c4878aSAndroid Build Coastguard Worker    });
65*61c4878aSAndroid Build Coastguard Worker  }
66*61c4878aSAndroid Build Coastguard Worker  static fromProtoDescriptor(
67*61c4878aSAndroid Build Coastguard Worker    descriptor: ServiceDescriptorProto,
68*61c4878aSAndroid Build Coastguard Worker    protoCollection: ProtoCollection,
69*61c4878aSAndroid Build Coastguard Worker    packageName: string,
70*61c4878aSAndroid Build Coastguard Worker  ) {
71*61c4878aSAndroid Build Coastguard Worker    const name = packageName + '.' + descriptor.getName()!;
72*61c4878aSAndroid Build Coastguard Worker    const methodList = descriptor
73*61c4878aSAndroid Build Coastguard Worker      .getMethodList()
74*61c4878aSAndroid Build Coastguard Worker      .map((methodDescriptor: MethodDescriptorProto) =>
75*61c4878aSAndroid Build Coastguard Worker        Method.protoDescriptorToRPCMethodDescriptor(
76*61c4878aSAndroid Build Coastguard Worker          methodDescriptor,
77*61c4878aSAndroid Build Coastguard Worker          protoCollection,
78*61c4878aSAndroid Build Coastguard Worker        ),
79*61c4878aSAndroid Build Coastguard Worker      );
80*61c4878aSAndroid Build Coastguard Worker    return new Service(name, methodList);
81*61c4878aSAndroid Build Coastguard Worker  }
82*61c4878aSAndroid Build Coastguard Worker}
83*61c4878aSAndroid Build Coastguard Worker
84*61c4878aSAndroid Build Coastguard Workerexport type MessageSerializer = {
85*61c4878aSAndroid Build Coastguard Worker  serialize: (message: Message) => Uint8Array;
86*61c4878aSAndroid Build Coastguard Worker  deserialize: (bytes: Uint8Array) => Message;
87*61c4878aSAndroid Build Coastguard Worker};
88*61c4878aSAndroid Build Coastguard Worker
89*61c4878aSAndroid Build Coastguard Workerexport type RPCMethodDescriptor = {
90*61c4878aSAndroid Build Coastguard Worker  name: string;
91*61c4878aSAndroid Build Coastguard Worker  requestType: MessageCreator;
92*61c4878aSAndroid Build Coastguard Worker  responseType: MessageCreator;
93*61c4878aSAndroid Build Coastguard Worker  customRequestSerializer?: MessageSerializer;
94*61c4878aSAndroid Build Coastguard Worker  customResponseSerializer?: MessageSerializer;
95*61c4878aSAndroid Build Coastguard Worker  clientStreaming?: boolean;
96*61c4878aSAndroid Build Coastguard Worker  serverStreaming?: boolean;
97*61c4878aSAndroid Build Coastguard Worker  protoDescriptor?: MethodDescriptorProto;
98*61c4878aSAndroid Build Coastguard Worker};
99*61c4878aSAndroid Build Coastguard Worker
100*61c4878aSAndroid Build Coastguard Workerexport enum MethodType {
101*61c4878aSAndroid Build Coastguard Worker  UNARY,
102*61c4878aSAndroid Build Coastguard Worker  SERVER_STREAMING,
103*61c4878aSAndroid Build Coastguard Worker  CLIENT_STREAMING,
104*61c4878aSAndroid Build Coastguard Worker  BIDIRECTIONAL_STREAMING,
105*61c4878aSAndroid Build Coastguard Worker}
106*61c4878aSAndroid Build Coastguard Worker
107*61c4878aSAndroid Build Coastguard Worker/** Describes an RPC method. */
108*61c4878aSAndroid Build Coastguard Workerexport class Method {
109*61c4878aSAndroid Build Coastguard Worker  readonly service: Service;
110*61c4878aSAndroid Build Coastguard Worker  readonly name: string;
111*61c4878aSAndroid Build Coastguard Worker  readonly id: number;
112*61c4878aSAndroid Build Coastguard Worker  readonly clientStreaming: boolean;
113*61c4878aSAndroid Build Coastguard Worker  readonly serverStreaming: boolean;
114*61c4878aSAndroid Build Coastguard Worker  readonly requestType: any;
115*61c4878aSAndroid Build Coastguard Worker  readonly responseType: any;
116*61c4878aSAndroid Build Coastguard Worker  readonly descriptor?: MethodDescriptorProto;
117*61c4878aSAndroid Build Coastguard Worker  readonly customRequestSerializer?: MessageSerializer;
118*61c4878aSAndroid Build Coastguard Worker  readonly customResponseSerializer?: MessageSerializer;
119*61c4878aSAndroid Build Coastguard Worker
120*61c4878aSAndroid Build Coastguard Worker  constructor(service: Service, methodDescriptor: RPCMethodDescriptor) {
121*61c4878aSAndroid Build Coastguard Worker    this.name = methodDescriptor.name;
122*61c4878aSAndroid Build Coastguard Worker    this.id = hash(this.name);
123*61c4878aSAndroid Build Coastguard Worker    this.service = service;
124*61c4878aSAndroid Build Coastguard Worker    this.serverStreaming = methodDescriptor.serverStreaming || false;
125*61c4878aSAndroid Build Coastguard Worker    this.clientStreaming = methodDescriptor.clientStreaming || false;
126*61c4878aSAndroid Build Coastguard Worker
127*61c4878aSAndroid Build Coastguard Worker    this.requestType = methodDescriptor.requestType;
128*61c4878aSAndroid Build Coastguard Worker    this.responseType = methodDescriptor.responseType;
129*61c4878aSAndroid Build Coastguard Worker    this.descriptor = methodDescriptor.protoDescriptor;
130*61c4878aSAndroid Build Coastguard Worker    this.customRequestSerializer = methodDescriptor.customRequestSerializer;
131*61c4878aSAndroid Build Coastguard Worker    this.customResponseSerializer = methodDescriptor.customResponseSerializer;
132*61c4878aSAndroid Build Coastguard Worker  }
133*61c4878aSAndroid Build Coastguard Worker
134*61c4878aSAndroid Build Coastguard Worker  static protoDescriptorToRPCMethodDescriptor(
135*61c4878aSAndroid Build Coastguard Worker    descriptor: MethodDescriptorProto,
136*61c4878aSAndroid Build Coastguard Worker    protoCollection: ProtoCollection,
137*61c4878aSAndroid Build Coastguard Worker  ): RPCMethodDescriptor {
138*61c4878aSAndroid Build Coastguard Worker    const requestTypePath = descriptor.getInputType()!;
139*61c4878aSAndroid Build Coastguard Worker    const responseTypePath = descriptor.getOutputType()!;
140*61c4878aSAndroid Build Coastguard Worker
141*61c4878aSAndroid Build Coastguard Worker    // Remove leading period if it exists.
142*61c4878aSAndroid Build Coastguard Worker    const requestType = protoCollection.getMessageCreator(
143*61c4878aSAndroid Build Coastguard Worker      requestTypePath.replace(/^\./, ''),
144*61c4878aSAndroid Build Coastguard Worker    )!;
145*61c4878aSAndroid Build Coastguard Worker    const responseType = protoCollection.getMessageCreator(
146*61c4878aSAndroid Build Coastguard Worker      responseTypePath.replace(/^\./, ''),
147*61c4878aSAndroid Build Coastguard Worker    )!;
148*61c4878aSAndroid Build Coastguard Worker
149*61c4878aSAndroid Build Coastguard Worker    return {
150*61c4878aSAndroid Build Coastguard Worker      name: descriptor.getName()!,
151*61c4878aSAndroid Build Coastguard Worker      serverStreaming: descriptor.getServerStreaming()!,
152*61c4878aSAndroid Build Coastguard Worker      clientStreaming: descriptor.getClientStreaming()!,
153*61c4878aSAndroid Build Coastguard Worker      requestType: requestType,
154*61c4878aSAndroid Build Coastguard Worker      responseType: responseType,
155*61c4878aSAndroid Build Coastguard Worker      protoDescriptor: descriptor,
156*61c4878aSAndroid Build Coastguard Worker    };
157*61c4878aSAndroid Build Coastguard Worker  }
158*61c4878aSAndroid Build Coastguard Worker
159*61c4878aSAndroid Build Coastguard Worker  static fromProtoDescriptor(
160*61c4878aSAndroid Build Coastguard Worker    descriptor: MethodDescriptorProto,
161*61c4878aSAndroid Build Coastguard Worker    protoCollection: ProtoCollection,
162*61c4878aSAndroid Build Coastguard Worker    service: Service,
163*61c4878aSAndroid Build Coastguard Worker  ) {
164*61c4878aSAndroid Build Coastguard Worker    return new Method(
165*61c4878aSAndroid Build Coastguard Worker      service,
166*61c4878aSAndroid Build Coastguard Worker      Method.protoDescriptorToRPCMethodDescriptor(descriptor, protoCollection),
167*61c4878aSAndroid Build Coastguard Worker    );
168*61c4878aSAndroid Build Coastguard Worker  }
169*61c4878aSAndroid Build Coastguard Worker
170*61c4878aSAndroid Build Coastguard Worker  get type(): MethodType {
171*61c4878aSAndroid Build Coastguard Worker    if (this.clientStreaming && this.serverStreaming) {
172*61c4878aSAndroid Build Coastguard Worker      return MethodType.BIDIRECTIONAL_STREAMING;
173*61c4878aSAndroid Build Coastguard Worker    } else if (this.clientStreaming && !this.serverStreaming) {
174*61c4878aSAndroid Build Coastguard Worker      return MethodType.CLIENT_STREAMING;
175*61c4878aSAndroid Build Coastguard Worker    } else if (!this.clientStreaming && this.serverStreaming) {
176*61c4878aSAndroid Build Coastguard Worker      return MethodType.SERVER_STREAMING;
177*61c4878aSAndroid Build Coastguard Worker    } else if (!this.clientStreaming && !this.serverStreaming) {
178*61c4878aSAndroid Build Coastguard Worker      return MethodType.UNARY;
179*61c4878aSAndroid Build Coastguard Worker    }
180*61c4878aSAndroid Build Coastguard Worker    throw Error('Unhandled streaming condition');
181*61c4878aSAndroid Build Coastguard Worker  }
182*61c4878aSAndroid Build Coastguard Worker}
183