Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Generate implementation for Name trait #931

Merged
merged 3 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
matrix:
toolchain:
- stable
- "1.64"
- "1.65"
os:
- ubuntu-latest
- macos-latest
Expand Down
39 changes: 39 additions & 0 deletions prost-build/src/code_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ impl<'a> CodeGenerator<'a> {
code_gen.package
);

if code_gen.config.enable_type_names {
code_gen.buf.push_str(&format!(
"const PACKAGE: &str = \"{}\";\n",
code_gen.package,
));
}

code_gen.path.push(4);
for (idx, message) in file.message_type.into_iter().enumerate() {
code_gen.path.push(idx as i32);
Expand Down Expand Up @@ -261,6 +268,38 @@ impl<'a> CodeGenerator<'a> {

self.pop_mod();
}

if self.config.enable_type_names {
self.append_type_name(&message_name, &fq_message_name);
}
}

fn append_type_name(&mut self, message_name: &str, fq_message_name: &str) {
self.buf.push_str(&format!(
"impl {}::Name for {} {{\n",
self.config.prost_path.as_deref().unwrap_or("::prost"),
to_upper_camel(&message_name)
));
self.depth += 1;

self.buf
.push_str("const PACKAGE: &'static str = PACKAGE;\n");
self.buf.push_str(&format!(
"const NAME: &'static str = \"{}\";\n",
message_name
));

if let Some(domain_name) = self.config.type_name_domains.get_first(fq_message_name) {
self.buf.push_str(&format!(
r#"fn type_url() -> String {{
format!("{}/{{}}", Self::full_name())
}}"#,
domain_name
));
}

self.depth -= 1;
self.buf.push_str("}\n");
}

fn append_type_attributes(&mut self, fq_message_name: &str) {
Expand Down
46 changes: 46 additions & 0 deletions prost-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,8 @@ pub struct Config {
out_dir: Option<PathBuf>,
extern_paths: Vec<(String, String)>,
default_package_filename: String,
enable_type_names: bool,
type_name_domains: PathMap<String>,
protoc_args: Vec<OsString>,
disable_comments: PathMap<()>,
skip_debug: PathMap<()>,
Expand Down Expand Up @@ -839,6 +841,46 @@ impl Config {
self
}

/// Configures the code generator to include type names.
///
/// Message types will implement `Name` trait, which provides type and package name.
/// This is needed for encoding messages as `Any` type.
pub fn enable_type_names(&mut self) -> &mut Self {
self.enable_type_names = true;
self
}

/// Specify domain names to use with message type URLs.
///
/// # Domains
///
/// **`paths`** - a path matching any number of types. It works the same way as in
/// [`btree_map`](#method.btree_map), just with the field name omitted.
///
/// **`domain`** - an arbitrary string to be used as a prefix for type URLs.
///
/// # Examples
///
/// ```rust
/// # let mut config = prost_build::Config::new();
/// // Full type URL of the message `google.profile.Person`,
/// // will be `type.googleapis.com/google.profile.Person`.
/// config.type_name_domain(&["."], "type.googleapis.com");
/// ```
pub fn type_name_domain<I, S, D>(&mut self, paths: I, domain: D) -> &mut Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
D: AsRef<str>,
{
self.type_name_domains.clear();
for matcher in paths {
self.type_name_domains
.insert(matcher.as_ref().to_string(), domain.as_ref().to_string());
}
self
}

/// Configures the path that's used for deriving `Message` for generated messages.
/// This is mainly useful for generating crates that wish to re-export prost.
/// Defaults to `::prost::Message` if not specified.
Expand Down Expand Up @@ -1257,6 +1299,8 @@ impl default::Default for Config {
out_dir: None,
extern_paths: Vec::new(),
default_package_filename: "_".to_string(),
enable_type_names: false,
type_name_domains: PathMap::default(),
protoc_args: Vec::new(),
disable_comments: PathMap::default(),
skip_debug: PathMap::default(),
Expand All @@ -1282,6 +1326,8 @@ impl fmt::Debug for Config {
.field("out_dir", &self.out_dir)
.field("extern_paths", &self.extern_paths)
.field("default_package_filename", &self.default_package_filename)
.field("enable_type_names", &self.enable_type_names)
.field("type_name_domains", &self.type_name_domains)
.field("protoc_args", &self.protoc_args)
.field("disable_comments", &self.disable_comments)
.field("skip_debug", &self.skip_debug)
Expand Down
6 changes: 6 additions & 0 deletions tests/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,12 @@ fn main() {
)
.unwrap();

prost_build::Config::new()
.enable_type_names()
.type_name_domain(&[".type_names.Foo"], "tests")
.compile_protos(&[src.join("type_names.proto")], includes)
.unwrap();

// Check that attempting to compile a .proto without a package declaration does not result in an error.
config
.compile_protos(&[src.join("no_package.proto")], includes)
Expand Down
2 changes: 2 additions & 0 deletions tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ mod no_unused_results;
#[cfg(test)]
#[cfg(feature = "std")]
mod skip_debug;
#[cfg(test)]
mod type_names;

mod test_enum_named_option_value {
include!(concat!(env!("OUT_DIR"), "/myenum.optionn.rs"));
Expand Down
9 changes: 9 additions & 0 deletions tests/src/type_names.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
syntax = "proto3";

package type_names;

message Foo {
}

message Bar {
}
15 changes: 15 additions & 0 deletions tests/src/type_names.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use prost::alloc::{format, string::String};
use prost::Name;

include!(concat!(env!("OUT_DIR"), "/type_names.rs"));

#[test]
fn valid_type_names() {
assert_eq!("Foo", Foo::NAME);
assert_eq!("type_names", Foo::PACKAGE);
assert_eq!("tests/type_names.Foo", Foo::type_url());

assert_eq!("Bar", Bar::NAME);
assert_eq!("type_names", Bar::PACKAGE);
assert_eq!("/type_names.Bar", Bar::type_url());
}