1# Copyright 2020 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"""This module generates the code for raw pw_rpc services.""" 15 16import os 17from typing import Iterable 18 19from pw_protobuf.output_file import OutputFile 20from pw_protobuf.proto_tree import ProtoNode, ProtoService, ProtoServiceMethod 21from pw_protobuf.proto_tree import build_node_tree 22from pw_rpc import codegen 23from pw_rpc.codegen import RPC_NAMESPACE 24 25PROTO_H_EXTENSION = '.pb.h' 26 27 28def _proto_filename_to_generated_header(proto_file: str) -> str: 29 """Returns the generated C++ RPC header name for a .proto file.""" 30 filename = os.path.splitext(proto_file)[0] 31 return f'{filename}.raw_rpc{PROTO_H_EXTENSION}' 32 33 34def _proto_filename_to_stub_header(proto_file: str) -> str: 35 """Returns the generated C++ RPC header name for a .proto file.""" 36 filename = os.path.splitext(proto_file)[0] 37 return f'{filename}.raw_rpc.stub{PROTO_H_EXTENSION}' 38 39 40def _generate_method_descriptor(method: ProtoServiceMethod, method_id: int, 41 output: OutputFile) -> None: 42 """Generates a method descriptor for a raw RPC method.""" 43 44 impl_method = f'&Implementation::{method.name()}' 45 46 output.write_line( 47 f'{RPC_NAMESPACE}::internal::GetRawMethodFor<{impl_method}, ' 48 f'{method.type().cc_enum()}>(') 49 output.write_line(f' 0x{method_id:08x}), // Hash of "{method.name()}"') 50 51 52def _generate_server_writer_alias(output: OutputFile) -> None: 53 output.write_line( 54 f'using RawServerWriter = {RPC_NAMESPACE}::RawServerWriter;') 55 56 57def _generate_code_for_client(unused_service: ProtoService, 58 unused_root: ProtoNode, 59 output: OutputFile) -> None: 60 """Outputs client code for an RPC service.""" 61 output.write_line('// Raw RPC clients are not yet implemented.\n') 62 63 64def _generate_code_for_service(service: ProtoService, root: ProtoNode, 65 output: OutputFile) -> None: 66 """Generates a C++ base class for a raw RPC service.""" 67 codegen.service_class(service, root, output, _generate_server_writer_alias, 68 'RawMethodUnion', _generate_method_descriptor) 69 70 71def _generate_code_for_package(proto_file, package: ProtoNode, 72 output: OutputFile) -> None: 73 """Generates code for a header file corresponding to a .proto file.""" 74 includes = lambda *_: ['#include "pw_rpc/internal/raw_method_union.h"'] 75 76 codegen.package(proto_file, package, output, includes, 77 _generate_code_for_service, _generate_code_for_client) 78 79 80class StubGenerator(codegen.StubGenerator): 81 def unary_signature(self, method: ProtoServiceMethod, prefix: str) -> str: 82 return (f'pw::StatusWithSize {prefix}{method.name()}(ServerContext&, ' 83 'pw::ConstByteSpan request, pw::ByteSpan response)') 84 85 def unary_stub(self, method: ProtoServiceMethod, 86 output: OutputFile) -> None: 87 output.write_line(codegen.STUB_REQUEST_TODO) 88 output.write_line('static_cast<void>(request);') 89 output.write_line(codegen.STUB_RESPONSE_TODO) 90 output.write_line('static_cast<void>(response);') 91 output.write_line('return pw::StatusWithSize::Unimplemented();') 92 93 def server_streaming_signature(self, method: ProtoServiceMethod, 94 prefix: str) -> str: 95 96 return (f'void {prefix}{method.name()}(ServerContext&, ' 97 'pw::ConstByteSpan request, RawServerWriter& writer)') 98 99 100def process_proto_file(proto_file) -> Iterable[OutputFile]: 101 """Generates code for a single .proto file.""" 102 103 _, package_root = build_node_tree(proto_file) 104 output_filename = _proto_filename_to_generated_header(proto_file.name) 105 output_file = OutputFile(output_filename) 106 _generate_code_for_package(proto_file, package_root, output_file) 107 108 output_file.write_line() 109 codegen.package_stubs(package_root, output_file, StubGenerator()) 110 111 return [output_file] 112