• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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