Skip to content

Commit acc1df8

Browse files
authored
Fix code generation for Name trait (#944)
1 parent 3cf34f0 commit acc1df8

File tree

4 files changed

+74
-37
lines changed

4 files changed

+74
-37
lines changed

prost-build/src/code_generator.rs

+51-26
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ enum Syntax {
2929
pub struct CodeGenerator<'a> {
3030
config: &'a mut Config,
3131
package: String,
32+
type_path: Vec<String>,
3233
source_info: Option<SourceCodeInfo>,
3334
syntax: Syntax,
3435
message_graph: &'a MessageGraph,
@@ -69,6 +70,7 @@ impl<'a> CodeGenerator<'a> {
6970
let mut code_gen = CodeGenerator {
7071
config,
7172
package: file.package.unwrap_or_default(),
73+
type_path: Vec::new(),
7274
source_info,
7375
syntax,
7476
message_graph,
@@ -84,13 +86,6 @@ impl<'a> CodeGenerator<'a> {
8486
code_gen.package
8587
);
8688

87-
if code_gen.config.enable_type_names {
88-
code_gen.buf.push_str(&format!(
89-
"const PACKAGE: &str = \"{}\";\n",
90-
code_gen.package,
91-
));
92-
}
93-
9489
code_gen.path.push(4);
9590
for (idx, message) in file.message_type.into_iter().enumerate() {
9691
code_gen.path.push(idx as i32);
@@ -128,10 +123,16 @@ impl<'a> CodeGenerator<'a> {
128123

129124
let message_name = message.name().to_string();
130125
let fq_message_name = format!(
131-
"{}{}.{}",
132-
if self.package.is_empty() { "" } else { "." },
133-
self.package,
134-
message.name()
126+
"{}{}{}{}.{}",
127+
if self.package.is_empty() && self.type_path.is_empty() {
128+
""
129+
} else {
130+
"."
131+
},
132+
self.package.trim_matches('.'),
133+
if self.type_path.is_empty() { "" } else { "." },
134+
self.type_path.join("."),
135+
message_name,
135136
);
136137

137138
// Skip external types.
@@ -282,19 +283,34 @@ impl<'a> CodeGenerator<'a> {
282283
));
283284
self.depth += 1;
284285

285-
self.buf
286-
.push_str("const PACKAGE: &'static str = PACKAGE;\n");
287286
self.buf.push_str(&format!(
288287
"const NAME: &'static str = \"{}\";\n",
289-
message_name
288+
message_name,
289+
));
290+
self.buf.push_str(&format!(
291+
"const PACKAGE: &'static str = \"{}\";\n",
292+
self.package,
293+
));
294+
295+
let prost_path = self.config.prost_path.as_deref().unwrap_or("::prost");
296+
let string_path = format!("{}::alloc::string::String", prost_path);
297+
let format_path = format!("{}::alloc::format", prost_path);
298+
299+
self.buf.push_str(&format!(
300+
r#"fn full_name() -> {string_path} {{
301+
{format_path}!("{}{}{}{}{{}}", Self::NAME)
302+
}}"#,
303+
self.package.trim_matches('.'),
304+
if self.package.is_empty() { "" } else { "." },
305+
self.type_path.join("."),
306+
if self.type_path.is_empty() { "" } else { "." },
290307
));
291308

292309
if let Some(domain_name) = self.config.type_name_domains.get_first(fq_message_name) {
293310
self.buf.push_str(&format!(
294-
r#"fn type_url() -> String {{
295-
format!("{}/{{}}", Self::full_name())
311+
r#"fn type_url() -> {string_path} {{
312+
{format_path}!("{domain_name}/{{}}", Self::full_name())
296313
}}"#,
297-
domain_name
298314
));
299315
}
300316

@@ -684,11 +700,18 @@ impl<'a> CodeGenerator<'a> {
684700

685701
let enum_values = &desc.value;
686702
let fq_proto_enum_name = format!(
687-
"{}{}.{}",
688-
if self.package.is_empty() { "" } else { "." },
689-
self.package,
690-
proto_enum_name
703+
"{}{}{}{}.{}",
704+
if self.package.is_empty() && self.type_path.is_empty() {
705+
""
706+
} else {
707+
"."
708+
},
709+
self.package.trim_matches('.'),
710+
if self.type_path.is_empty() { "" } else { "." },
711+
self.type_path.join("."),
712+
proto_enum_name,
691713
);
714+
692715
if self
693716
.extern_paths
694717
.resolve_ident(&fq_proto_enum_name)
@@ -906,17 +929,15 @@ impl<'a> CodeGenerator<'a> {
906929
self.buf.push_str(&to_snake(module));
907930
self.buf.push_str(" {\n");
908931

909-
self.package.push('.');
910-
self.package.push_str(module);
932+
self.type_path.push(module.into());
911933

912934
self.depth += 1;
913935
}
914936

915937
fn pop_mod(&mut self) {
916938
self.depth -= 1;
917939

918-
let idx = self.package.rfind('.').unwrap();
919-
self.package.truncate(idx);
940+
self.type_path.pop();
920941

921942
self.push_indent();
922943
self.buf.push_str("}\n");
@@ -954,7 +975,11 @@ impl<'a> CodeGenerator<'a> {
954975
return proto_ident;
955976
}
956977

957-
let mut local_path = self.package.split('.').peekable();
978+
let mut local_path = self
979+
.package
980+
.split('.')
981+
.chain(self.type_path.iter().map(String::as_str))
982+
.peekable();
958983

959984
// If no package is specified the start of the package name will be '.'
960985
// and split will return an empty string ("") which breaks resolution

src/name.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,27 @@ use alloc::{format, string::String};
55

66
/// Associate a type name with a [`Message`] type.
77
pub trait Name: Message {
8-
/// Type name for this [`Message`]. This is the camel case name,
9-
/// e.g. `TypeName`.
8+
/// Simple name for this [`Message`].
9+
/// This name is the same as it appears in the source .proto file, e.g. `FooBar`.
1010
const NAME: &'static str;
1111

1212
/// Package name this message type is contained in. They are domain-like
1313
/// and delimited by `.`, e.g. `google.protobuf`.
1414
const PACKAGE: &'static str;
1515

16-
/// Full name of this message type containing both the package name and
17-
/// type name, e.g. `google.protobuf.TypeName`.
16+
/// Fully-qualified unique name for this [`Message`].
17+
/// It's prefixed with the package name and names of any parent messages,
18+
/// e.g. `google.rpc.BadRequest.FieldViolation`.
19+
/// By default, this is the package name followed by the message name.
20+
/// Fully-qualified names must be unique within a domain of Type URLs.
1821
fn full_name() -> String {
1922
format!("{}.{}", Self::PACKAGE, Self::NAME)
2023
}
2124

22-
/// Type URL for this message, which by default is the full name with a
25+
/// Type URL for this [`Message`], which by default is the full name with a
2326
/// leading slash, but may also include a leading domain name, e.g.
2427
/// `type.googleapis.com/google.profile.Person`.
28+
/// This can be used when serializing with the [`Any`] type.
2529
fn type_url() -> String {
2630
format!("/{}", Self::full_name())
2731
}

tests/src/type_names.proto

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ syntax = "proto3";
33
package type_names;
44

55
message Foo {
6+
message Bar {
7+
}
68
}
79

8-
message Bar {
9-
}
10+
message Baz {
11+
}

tests/src/type_names.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use prost::alloc::{format, string::String};
21
use prost::Name;
32

43
include!(concat!(env!("OUT_DIR"), "/type_names.rs"));
@@ -7,9 +6,16 @@ include!(concat!(env!("OUT_DIR"), "/type_names.rs"));
76
fn valid_type_names() {
87
assert_eq!("Foo", Foo::NAME);
98
assert_eq!("type_names", Foo::PACKAGE);
9+
assert_eq!("type_names.Foo", Foo::full_name());
1010
assert_eq!("tests/type_names.Foo", Foo::type_url());
1111

12-
assert_eq!("Bar", Bar::NAME);
13-
assert_eq!("type_names", Bar::PACKAGE);
14-
assert_eq!("/type_names.Bar", Bar::type_url());
12+
assert_eq!("Bar", foo::Bar::NAME);
13+
assert_eq!("type_names", foo::Bar::PACKAGE);
14+
assert_eq!("type_names.Foo.Bar", foo::Bar::full_name());
15+
assert_eq!("tests/type_names.Foo.Bar", foo::Bar::type_url());
16+
17+
assert_eq!("Baz", Baz::NAME);
18+
assert_eq!("type_names", Baz::PACKAGE);
19+
assert_eq!("type_names.Baz", Baz::full_name());
20+
assert_eq!("/type_names.Baz", Baz::type_url());
1521
}

0 commit comments

Comments
 (0)