@@ -14,7 +14,10 @@ defmodule GrpcReflection.Service.Builder do
1414 new_state = process_service ( service )
1515 State . merge ( state , new_state )
1616 end )
17+ # shrink_cycles must run first so the symbol table reflects final merged filenames
18+ # before resolve_dependencies rewrites dep strings through it
1719 |> State . shrink_cycles ( )
20+ |> resolve_dependencies ( )
1821
1922 { :ok , tree }
2023 end
@@ -76,40 +79,48 @@ defmodule GrpcReflection.Service.Builder do
7679 end
7780
7881 defp trace_message_fields ( state , parent_symbol , module , fields ) do
79- # nested types arent a "separate file", they return their parents' response
8082 nested_types = Util . get_nested_types ( parent_symbol , module . descriptor ( ) )
8183
8284 module . __message_props__ ( ) . field_props
8385 |> Map . values ( )
8486 |> Enum . map ( fn % { name: name , type: type } ->
85- % {
86- mod:
87- case type do
88- { _ , mod } -> mod
89- mod -> mod
90- end ,
91- symbol: Enum . find ( fields , fn f -> f . name == name end ) . type_name
92- }
93- end )
94- |> Enum . reject ( fn % { symbol: s } -> is_nil ( s ) or State . has_symbol? ( state , s ) end )
95- |> Enum . reduce ( state , fn % { mod: mod , symbol: symbol } , state ->
96- symbol = Util . trim_symbol ( symbol )
97-
98- response =
99- if symbol in nested_types do
100- build_response ( parent_symbol , module )
101- else
102- build_response ( symbol , mod )
87+ mod =
88+ case type do
89+ { _ , mod } -> mod
90+ mod -> mod
10391 end
10492
105- state
106- |> Extensions . add_extensions ( symbol , mod )
107- |> State . add_file ( response )
108- |> State . add_symbol ( symbol , response . name )
109- |> trace_message_refs ( symbol , mod )
93+ % { mod: mod , symbol: Enum . find ( fields , fn f -> f . name == name end ) . type_name }
94+ end )
95+ |> Enum . reject ( fn % { symbol: s } -> is_nil ( s ) end )
96+ |> Enum . reduce ( state , fn % { mod: mod , symbol: symbol } , state ->
97+ trace_field_ref ( state , parent_symbol , Util . trim_symbol ( symbol ) , mod , nested_types )
11098 end )
11199 end
112100
101+ defp trace_field_ref ( state , _parent_symbol , symbol , _mod , _nested_types )
102+ when is_map_key ( state . symbols , symbol ) ,
103+ do: state
104+
105+ defp trace_field_ref ( state , parent_symbol , symbol , mod , nested_types ) do
106+ { file , filename } =
107+ if symbol in nested_types do
108+ # nested types belong in the same file as their ancestor — look it up rather
109+ # than building a new synthetic file (which would have wrong FQDNs)
110+ parent_filename = state . symbols [ parent_symbol ]
111+ { state . files [ parent_filename ] , parent_filename }
112+ else
113+ response = build_response ( symbol , mod )
114+ { response , response . name }
115+ end
116+
117+ state
118+ |> Extensions . add_extensions ( symbol , mod )
119+ |> State . add_file ( file )
120+ |> State . add_symbol ( symbol , filename )
121+ |> trace_message_refs ( symbol , mod )
122+ end
123+
113124 defp build_response ( symbol , module ) do
114125 # we build our own file responses, so unwrap any present
115126 descriptor = get_descriptor ( module )
@@ -140,6 +151,27 @@ defmodule GrpcReflection.Service.Builder do
140151 end
141152 end
142153
154+ # Rewrite each file's dependency list to use actual filenames from the symbol table.
155+ # build_response names deps as `type_symbol <> ".proto"`, but nested types share a file
156+ # with their ancestor, so that filename may not exist. Must run after shrink_cycles so
157+ # merged cycle filenames are already reflected in the symbol table.
158+ defp resolve_dependencies ( % State { files: files , symbols: symbols } = state ) do
159+ resolved_files =
160+ Map . new ( files , fn { filename , descriptor } ->
161+ resolved_deps =
162+ descriptor . dependency
163+ |> Enum . map ( fn dep ->
164+ type_symbol = String . trim_trailing ( dep , ".proto" )
165+ symbols [ type_symbol ] || dep
166+ end )
167+ |> Enum . uniq ( )
168+
169+ { filename , % { descriptor | dependency: resolved_deps } }
170+ end )
171+
172+ % { state | files: resolved_files }
173+ end
174+
143175 # protoc with the elixir generator and protobuf.generate slightly differ for how they
144176 # generate descriptors. Use this to potentially unwrap the service proto when dealing
145177 # with descriptors that could come from a service module.
0 commit comments