Skip to content

Commit 7b3155d

Browse files
committed
compiler: fix breaking changes caused by bumping protobuf version
Bumping protobuf and protobuf-codegen to 3.7.2 breaks the ttrpc-codegen and ttrpc-compiler crates. Both of these crates were using the 2.27.1 version of the protobuf crates. This commit fixes the breaking changes introduced by the bump in major version. Signed-off-by: Jorge Prendes <jorge.prendes@gmail.com>
1 parent 5e08cd6 commit 7b3155d

File tree

8 files changed

+410
-201
lines changed

8 files changed

+410
-201
lines changed

compiler/src/codegen.rs

Lines changed: 79 additions & 102 deletions
Large diffs are not rendered by default.

compiler/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
pub mod codegen;
2727
pub mod prost_codegen;
2828
mod util;
29+
mod vendored;
2930

3031
/// Customize generated code.
3132
#[derive(Default, Debug, Clone)]

compiler/src/util.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@
1313
// See the License for the specific language governing permissions and
1414
// limitations under the License.
1515

16-
use protobuf_codegen::code_writer::CodeWriter;
1716
use std::fmt;
1817
use std::str;
1918

19+
use crate::vendored::CodeWriter;
20+
2021
// A struct that divide a name into serveral parts that meets rust's guidelines.
2122
struct NameSpliter<'a> {
2223
name: &'a [u8],
@@ -111,9 +112,9 @@ where
111112
F: Fn(&mut CodeWriter),
112113
{
113114
if public {
114-
w.expr_block(&format!("pub async fn {}", sig), cb);
115+
w.expr_block(format!("pub async fn {}", sig), cb);
115116
} else {
116-
w.expr_block(&format!("async fn {}", sig), cb);
117+
w.expr_block(format!("async fn {}", sig), cb);
117118
}
118119
}
119120

compiler/src/vendored.rs

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
//! This module contains functionalities that where previously available in
2+
//! the protobuf / protobuf-codegen crates, but were then removed.
3+
//! The missing functionalities have been reimplemented in this module.
4+
5+
use protobuf::descriptor::{DescriptorProto, FileDescriptorProto};
6+
use protobuf_codegen::proto_name_to_rs;
7+
8+
// proto_name_to_rs is constructor as "{proto_path_to_rust_mod}.rs"
9+
// see https://github.com/stepancheg/rust-protobuf/blob/v3.7.2/protobuf-codegen/src/gen/paths.rs#L43
10+
pub fn proto_path_to_rust_mod(path: &str) -> String {
11+
proto_name_to_rs(path)
12+
.strip_suffix(".rs")
13+
.unwrap()
14+
.to_string()
15+
}
16+
17+
// vendered from https://github.com/stepancheg/rust-protobuf/blob/v3.7.2/protobuf-codegen/src/gen/rust/keywords.rs
18+
fn is_rust_keyword(ident: &str) -> bool {
19+
#[rustfmt::skip]
20+
static RUST_KEYWORDS: &[&str] = &[
21+
"_",
22+
"as",
23+
"async",
24+
"await",
25+
"break",
26+
"crate",
27+
"dyn",
28+
"else",
29+
"enum",
30+
"extern",
31+
"false",
32+
"fn",
33+
"for",
34+
"if",
35+
"impl",
36+
"in",
37+
"let",
38+
"loop",
39+
"match",
40+
"mod",
41+
"move",
42+
"mut",
43+
"pub",
44+
"ref",
45+
"return",
46+
"static",
47+
"self",
48+
"Self",
49+
"struct",
50+
"super",
51+
"true",
52+
"trait",
53+
"type",
54+
"unsafe",
55+
"use",
56+
"while",
57+
"continue",
58+
"box",
59+
"const",
60+
"where",
61+
"virtual",
62+
"proc",
63+
"alignof",
64+
"become",
65+
"offsetof",
66+
"priv",
67+
"pure",
68+
"sizeof",
69+
"typeof",
70+
"unsized",
71+
"yield",
72+
"do",
73+
"abstract",
74+
"final",
75+
"override",
76+
"macro",
77+
];
78+
RUST_KEYWORDS.contains(&ident)
79+
}
80+
81+
// adapted from https://github.com/stepancheg/rust-protobuf/blob/v3.7.2/protobuf-codegen/src/gen/code_writer.rs#L12
82+
#[derive(Default)]
83+
pub struct CodeWriter {
84+
writer: String,
85+
indent: String,
86+
}
87+
88+
impl CodeWriter {
89+
pub fn new() -> CodeWriter {
90+
Self::default()
91+
}
92+
93+
pub fn code(&self) -> &str {
94+
&self.writer
95+
}
96+
97+
pub fn take_code(&mut self) -> String {
98+
std::mem::take(&mut self.writer)
99+
}
100+
101+
pub fn write_line(&mut self, line: impl AsRef<str>) {
102+
if line.as_ref().is_empty() {
103+
self.writer.push('\n');
104+
} else {
105+
self.writer.push_str(&self.indent);
106+
self.writer.push_str(line.as_ref());
107+
self.writer.push('\n');
108+
}
109+
}
110+
111+
pub fn block(
112+
&mut self,
113+
first_line: impl AsRef<str>,
114+
last_line: impl AsRef<str>,
115+
cb: impl FnOnce(&mut CodeWriter),
116+
) {
117+
self.write_line(first_line);
118+
self.indented(cb);
119+
self.write_line(last_line);
120+
}
121+
122+
pub fn expr_block(&mut self, prefix: impl AsRef<str>, cb: impl FnOnce(&mut CodeWriter)) {
123+
self.block(format!("{} {{", prefix.as_ref()), "}", cb);
124+
}
125+
126+
pub fn indented(&mut self, cb: impl FnOnce(&mut CodeWriter)) {
127+
self.indent.push_str(" ");
128+
cb(self);
129+
self.indent.truncate(self.indent.len() - 4);
130+
}
131+
132+
pub fn pub_fn(&mut self, sig: impl AsRef<str>, cb: impl FnOnce(&mut CodeWriter)) {
133+
self.expr_block(format!("pub fn {}", sig.as_ref()), cb)
134+
}
135+
136+
pub fn def_fn(&mut self, sig: impl AsRef<str>, cb: impl FnOnce(&mut CodeWriter)) {
137+
self.expr_block(format!("fn {}", sig.as_ref()), cb)
138+
}
139+
140+
pub fn pub_struct(&mut self, name: impl AsRef<str>, cb: impl FnOnce(&mut CodeWriter)) {
141+
self.expr_block(format!("pub struct {}", name.as_ref()), cb);
142+
}
143+
144+
pub fn field_decl(&mut self, name: impl AsRef<str>, field_type: impl AsRef<str>) {
145+
self.write_line(format!("{}: {},", name.as_ref(), field_type.as_ref()));
146+
}
147+
148+
pub fn impl_self_block(&mut self, name: impl AsRef<str>, cb: impl FnOnce(&mut CodeWriter)) {
149+
self.expr_block(format!("impl {}", name.as_ref()), cb);
150+
}
151+
152+
pub fn pub_trait(&mut self, name: impl AsRef<str>, cb: impl FnOnce(&mut CodeWriter)) {
153+
self.expr_block(format!("pub trait {}", name.as_ref()), cb);
154+
}
155+
}
156+
157+
// reimplementation based on https://github.com/stepancheg/rust-protobuf/blob/v3.7.2/protobuf-codegen/src/gen/scope.rs#L26
158+
// it only implements the `find_message` method with not extra dependencies
159+
pub struct RootScope<'a> {
160+
pub file_descriptors: &'a [FileDescriptorProto],
161+
}
162+
163+
// re-implementation of https://github.com/stepancheg/rust-protobuf/blob/v3.7.2/protobuf-codegen/src/gen/scope.rs#L340
164+
// also based on https://github.com/stepancheg/rust-protobuf/blob/v3.7.2/protobuf-codegen/src/gen/scope.rs#L156
165+
pub struct ScopedMessage<'a> {
166+
pub fd: &'a FileDescriptorProto,
167+
pub path: Vec<&'a DescriptorProto>,
168+
pub msg: &'a DescriptorProto,
169+
}
170+
171+
impl ScopedMessage<'_> {
172+
pub fn prefix(&self) -> String {
173+
let mut prefix = String::new();
174+
for m in &self.path {
175+
prefix.push_str(m.name());
176+
prefix.push('.');
177+
}
178+
prefix
179+
}
180+
181+
// rust type name prefix for this scope
182+
pub fn rust_prefix(&self) -> String {
183+
self.prefix().replace(".", "_")
184+
}
185+
186+
// rust type name of this descriptor
187+
pub fn rust_name(&self) -> String {
188+
let mut r = self.rust_prefix();
189+
// Only escape if prefix is not empty
190+
if r.is_empty() && is_rust_keyword(self.msg.name()) {
191+
r.push_str("message_");
192+
}
193+
r.push_str(self.msg.name());
194+
r
195+
}
196+
197+
// fully-qualified name of this type
198+
pub fn rust_fq_name(&self) -> String {
199+
format!(
200+
"{}::{}",
201+
proto_path_to_rust_mod(self.fd.name()),
202+
self.rust_name()
203+
)
204+
}
205+
}
206+
207+
impl<'a> RootScope<'a> {
208+
pub fn find_message(&'a self, fqn: impl AsRef<str>) -> ScopedMessage<'a> {
209+
let Some(fqn1) = fqn.as_ref().strip_prefix(".") else {
210+
panic!("name must start with dot: {}", fqn.as_ref())
211+
};
212+
for fd in self.file_descriptors {
213+
let mut fqn2 = match fqn1.strip_prefix(fd.package()) {
214+
Some(rest) if fd.package().is_empty() => rest,
215+
Some(rest) if rest.starts_with(".") => &rest[1..],
216+
_ => continue,
217+
};
218+
219+
assert!(!fqn2.starts_with("."));
220+
221+
let mut pending = Some(fd.message_type.as_slice());
222+
let mut path = vec![];
223+
while let Some(msgs) = pending.take() {
224+
for msg in msgs {
225+
fqn2 = match fqn2.strip_prefix(msg.name()) {
226+
Some("") => return ScopedMessage { msg, path, fd },
227+
Some(rest) if rest.starts_with(".") => &rest[1..],
228+
_ => continue,
229+
};
230+
path.push(msg);
231+
pending = Some(&msg.nested_type);
232+
break;
233+
}
234+
}
235+
}
236+
panic!("enum not found by name: {}", fqn.as_ref())
237+
}
238+
}

example/Cargo.toml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,3 @@ path = "./async-stream-client.rs"
5151

5252
[build-dependencies]
5353
ttrpc-codegen = { path = "../ttrpc-codegen"}
54-
55-
[patch.crates-io]
56-
ttrpc-compiler = { path = "../compiler"}
57-

ttrpc-codegen/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ readme = "README.md"
1616
# lock home to avoid conflict with latest version
1717
home = "=0.5.9"
1818
protobuf-support = "3.7.2"
19-
protobuf = { version = "3.7.2" }
19+
protobuf = "3.7.2"
2020
protobuf-codegen = "3.7.2"
21-
ttrpc-compiler = "0.7.0"
21+
ttrpc-compiler = { version = "0.7.0", path = "../compiler" }

0 commit comments

Comments
 (0)