Skip to content

Commit ffe23ba

Browse files
committed
feat: add full_name callback to proto messages
closes #408
1 parent 64d4ced commit ffe23ba

File tree

10 files changed

+101
-19
lines changed

10 files changed

+101
-19
lines changed

lib/protobuf.ex

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ defmodule Protobuf do
8282
Protobuf.Builder.new!(__MODULE__, attrs)
8383
end
8484

85+
@impl unquote(__MODULE__)
86+
def full_name(), do: @options[:full_name]
87+
8588
@impl unquote(__MODULE__)
8689
def transform_module() do
8790
nil
@@ -175,6 +178,11 @@ defmodule Protobuf do
175178
"""
176179
@callback transform_module() :: module() | nil
177180

181+
@doc """
182+
Returns the fully qualified name of the descriptor's target.
183+
"""
184+
@callback full_name() :: String.t() | nil
185+
178186
@doc """
179187
Decodes the given binary data interpreting it as the Protobuf message `module`.
180188

lib/protobuf/protoc/generator/enum.ex

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,17 @@ defmodule Protobuf.Protoc.Generator.Enum do
1919
def generate(%Context{namespace: ns} = ctx, %Google.Protobuf.EnumDescriptorProto{} = desc) do
2020
msg_name = Util.mod_name(ctx, ns ++ [Macro.camelize(desc.name)])
2121

22+
full_name =
23+
([ctx.package] ++ ctx.namespace ++ [desc.name])
24+
|> Enum.reject(&is_nil/1)
25+
|> Enum.join(".")
26+
2227
use_options =
2328
Util.options_to_str(%{
2429
syntax: ctx.syntax,
2530
enum: true,
26-
protoc_gen_elixir_version: "\"#{Util.version()}\""
31+
protoc_gen_elixir_version: "\"#{Util.version()}\"",
32+
full_name: "\"#{full_name}\""
2733
})
2834

2935
descriptor_fun_body =

lib/protobuf/protoc/generator/message.ex

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ defmodule Protobuf.Protoc.Generator.Message do
3636
fields = get_fields(ctx, desc)
3737
extensions = get_extensions(desc)
3838

39+
full_name =
40+
([ctx.package] ++ ctx.namespace ++ [desc.name])
41+
|> Enum.reject(&is_nil/1)
42+
|> Enum.join(".")
43+
3944
descriptor_fun_body =
4045
if ctx.gen_descriptors? do
4146
Util.descriptor_fun_body(desc)
@@ -52,7 +57,7 @@ defmodule Protobuf.Protoc.Generator.Message do
5257
message_template(
5358
comment: Comment.get(ctx),
5459
module: msg_name,
55-
use_options: msg_opts_str(ctx, desc.options),
60+
use_options: msg_opts_str(ctx, desc.options, full_name),
5661
oneofs: desc.oneof_decl,
5762
fields: gen_fields(ctx.syntax, fields),
5863
descriptor_fun_body: descriptor_fun_body,
@@ -98,14 +103,15 @@ defmodule Protobuf.Protoc.Generator.Message do
98103
":#{f[:name]}, #{f[:number]}, #{label_str}type: #{f[:type]}#{opts_str}"
99104
end
100105

101-
defp msg_opts_str(%{syntax: syntax}, opts) do
106+
defp msg_opts_str(%{syntax: syntax}, opts, full_name) do
102107
msg_options = opts
103108

104109
opts = %{
105110
syntax: syntax,
106111
map: msg_options && msg_options.map_entry,
107112
deprecated: msg_options && msg_options.deprecated,
108-
protoc_gen_elixir_version: "\"#{Util.version()}\""
113+
protoc_gen_elixir_version: "\"#{Util.version()}\"",
114+
full_name: "\"#{full_name}\""
109115
}
110116

111117
str = Util.options_to_str(opts)

mix.exs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,12 @@ defmodule Protobuf.Mixfile do
172172
"./generated",
173173
["test/protobuf/protoc/proto/no_package.proto"]
174174
)
175+
176+
protoc!(
177+
"-I test/protobuf/protoc/proto --elixir_opt=include_docs=true",
178+
"./generated",
179+
["test/protobuf/protoc/proto/full_name.proto"]
180+
)
175181
end
176182

177183
defp gen_bench_protos(_args) do

test/protobuf/protobuf_test.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ defmodule Protobuf.ProtobufTest do
22
use ExUnit.Case, async: false
33

44
test "load_extensions/0 is a noop" do
5-
assert loaded_extensions() == 18
5+
assert loaded_extensions() == 19
66
Protobuf.load_extensions()
7-
assert loaded_extensions() == 18
7+
assert loaded_extensions() == 19
88
end
99

1010
describe "encode/1" do

test/protobuf/protoc/generator/enum_test.exs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,21 @@ defmodule Protobuf.Protoc.Generator.EnumTest do
2828
assert [{compiled_mod, bytecode}] = Code.compile_string(msg)
2929
assert inspect(compiled_mod) == module
3030

31-
assert msg =~ "defmodule #{module} do\n"
32-
assert msg =~ "use Protobuf, enum: true, protoc_gen_elixir_version: \"#{Util.version()}\"\n"
31+
assert msg == """
32+
defmodule ProtobufProtocGeneratorEnumTestEnumFoo do
33+
@moduledoc false
3334
34-
refute msg =~ "defstruct "
35+
use Protobuf,
36+
enum: true,
37+
full_name: "ProtobufProtocGeneratorEnumTestEnumFoo",
38+
protoc_gen_elixir_version: "#{Util.version()}"
3539
36-
assert msg =~ """
3740
field :A, 0
3841
field :B, 1
3942
field :HAS_UNDERSCORES, 2
4043
field :HAS_UNDERSCORES_X, 3
4144
field :HAS_UNDERSCORES_, 4
45+
end
4246
"""
4347

4448
assert TestHelpers.get_type_spec_as_string(compiled_mod, bytecode, :t) ==
@@ -68,7 +72,9 @@ defmodule Protobuf.Protoc.Generator.EnumTest do
6872
assert inspect(compiled_mod) == module
6973

7074
assert msg =~ "defmodule #{module} do\n"
71-
assert msg =~ "use Protobuf, enum: true, protoc_gen_elixir_version: \"#{Util.version()}\"\n"
75+
76+
assert msg =~
77+
"use Protobuf,\n enum: true,\n full_name: \"ProtobufProtocGeneratorEnumTestEnumFooDesc\",\n protoc_gen_elixir_version: \"#{Util.version()}\"\n\n"
7278

7379
refute msg =~ "defstruct "
7480

test/protobuf/protoc/generator/message_test.exs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,21 @@ defmodule Protobuf.Protoc.Generator.MessageTest do
77
alias Protobuf.TestHelpers
88

99
test "generate/2 has right name" do
10-
ctx = %Context{}
10+
ctx = %Context{package: "pkg.name"}
1111
desc = %Google.Protobuf.DescriptorProto{name: "Foo"}
1212
{[], [{_mod, msg}]} = Generator.generate(ctx, desc)
13-
assert msg =~ "defmodule Foo do\n"
14-
assert msg =~ "use Protobuf, protoc_gen_elixir_version: \"#{Util.version()}\"\n"
13+
assert msg =~ "defmodule Pkg.Name.Foo do\n"
14+
15+
assert msg =~
16+
"use Protobuf, full_name: \"pkg.name.Foo\", protoc_gen_elixir_version: \"#{Util.version()}\"\n"
1517

1618
assert [{compiled_mod, bytecode}] = Code.compile_string(msg)
1719

1820
assert TestHelpers.get_type_spec_as_string(compiled_mod, bytecode, :t) ==
1921
Macro.to_string(
2022
quote(
2123
do:
22-
t() :: %Foo{
24+
t() :: %Pkg.Name.Foo{
2325
__unknown_fields__: [Protobuf.unknown_field()]
2426
}
2527
)
@@ -81,7 +83,9 @@ defmodule Protobuf.Protoc.Generator.MessageTest do
8183
}
8284

8385
{[], [{_mod, msg}]} = Generator.generate(ctx, desc)
84-
assert msg =~ "use Protobuf, map: true, protoc_gen_elixir_version: \"#{Util.version()}\"\n"
86+
87+
assert msg =~
88+
"use Protobuf, full_name: \"pkg.name.Foo\", map: true, protoc_gen_elixir_version: \"#{Util.version()}\"\n"
8589
end
8690

8791
test "generate/2 has right fields" do
@@ -611,12 +615,16 @@ defmodule Protobuf.Protoc.Generator.MessageTest do
611615
}
612616

613617
{[[{_mod, msg}]], _} = Generator.generate(ctx, desc)
614-
assert msg =~ "defmodule Foo.Nested.EnumFoo do\n"
615-
assert msg =~ "use Protobuf, enum: true, protoc_gen_elixir_version: \"#{Util.version()}\"\n"
616618

617-
assert msg =~ """
619+
assert msg == """
620+
defmodule Foo.Nested.EnumFoo do
621+
@moduledoc false
622+
623+
use Protobuf, enum: true, full_name: "Foo.Nested.EnumFoo", protoc_gen_elixir_version: "#{Util.version()}"
624+
618625
field :a, 0
619626
field :b, 1
627+
end
620628
"""
621629
end
622630

test/protobuf/protoc/generator_integration_test.exs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,11 @@ defmodule Protobuf.Protoc.GeneratorIntegrationTest do
8787
assert NoPackageMessage.NumberMappingEntry.__message_props__().map?
8888
assert NoPackageMessage.decode(output) == input
8989
end
90+
91+
test "full_name/0 returns expected value for generated modules from full_name.proto" do
92+
assert Foo.Bar.Unit.full_name() == "foo.bar.Unit"
93+
assert Foo.Bar.Message.full_name() == "foo.bar.Message"
94+
assert Foo.Bar.Message.NestedMessage.full_name() == "foo.bar.Message.NestedMessage"
95+
assert Foo.Bar.Message.NestedMessage.Kind.full_name() == "foo.bar.Message.NestedMessage.Kind"
96+
end
9097
end
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// From: https://protobuf.com/docs/language-spec#fully-qualified-names
2+
syntax = "proto3"; // Fully-qualified name
3+
//----------------------
4+
package foo.bar; // foo.bar
5+
//
6+
import "google/protobuf/descriptor.proto"; //
7+
//
8+
message Message { // foo.bar.Message
9+
oneof id { // foo.bar.Message.id
10+
string name = 1; // foo.bar.Message.name
11+
uint64 num = 2; // foo.bar.Message.num
12+
} //
13+
message NestedMessage { // foo.bar.Message.NestedMessage
14+
extend google.protobuf.MessageOptions { //
15+
string fizz = 49999; // foo.bar.Message.NestedMessage.fizz
16+
} //
17+
option (NestedMessage.fizz) = "buzz"; //
18+
enum Kind { // foo.bar.Message.NestedMessage.Kind
19+
NULL = 0; // foo.bar.Message.NestedMessage.NULL
20+
PRIMARY = 1; // foo.bar.Message.NestedMessage.PRIMARY
21+
SECONDARY = 2; // foo.bar.Message.NestedMessage.SECONDARY
22+
} //
23+
Kind kind = 1; // foo.bar.Message.NestedMessage.kind
24+
} //
25+
NestedMessage extra = 3; // foo.bar.Message.extra
26+
} //
27+
//
28+
enum Unit { // foo.bar.Unit
29+
VOID = 0; // foo.bar.VOID
30+
} //
31+
//
32+
service FooService { // foo.bar.FooService
33+
rpc Bar(Message) returns (Message); // foo.bar.FooService.Bar
34+
} //

test/protobuf/protoc/proto/test.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,4 @@ enum MapEnum {
153153
HELLO = 0;
154154
WORLD = 2;
155155
}
156+

0 commit comments

Comments
 (0)