1%%% Copyright (C) 2017 Tomas Abrahamsson 2%%% 3%%% Author: Tomas Abrahamsson <tab@lysator.liu.se> 4%%% 5%%% This library is free software; you can redistribute it and/or 6%%% modify it under the terms of the GNU Lesser General Public 7%%% License as published by the Free Software Foundation; either 8%%% version 2.1 of the License, or (at your option) any later version. 9%%% 10%%% This library is distributed in the hope that it will be useful, 11%%% but WITHOUT ANY WARRANTY; without even the implied warranty of 12%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13%%% Lesser General Public License for more details. 14%%% 15%%% You should have received a copy of the GNU Lesser General Public 16%%% License along with this library; if not, write to the Free Software 17%%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 18%%% MA 02110-1301 USA 19 20%%% @doc Generation of introspection functions. 21%%% @private 22 23-module(gpb_gen_introspect). 24 25-export([format_enum_value_symbol_converter_exports/1]). 26-export([format_introspection/2]). 27 28-include("../include/gpb.hrl"). 29-include("gpb_codegen.hrl"). 30-include("gpb_compile.hrl"). 31 32-import(gpb_lib, [replace_term/2, replace_tree/2, repeat_clauses/2]). 33 34format_introspection(Defs, Opts) -> 35 MsgDefs = [Item || {{msg, _}, _}=Item <- Defs], 36 EnumDefs = [Item || {{enum, _}, _}=Item <- Defs], 37 GroupDefs = [Item || {{group, _}, _}=Item <- Defs], 38 ServiceDefs = [Item || {{service, _}, _}=Item <- Defs], 39 [gpb_codegen:format_fn( 40 get_msg_defs, fun() -> '<Defs>' end, 41 [replace_tree('<Defs>', msg_def_trees(EnumDefs, MsgDefs, GroupDefs, 42 Opts))]), 43 "\n", 44 gpb_codegen:format_fn( 45 get_msg_names, fun() -> '<Names>' end, 46 [replace_term('<Names>', [MsgName || {{msg,MsgName}, _} <- Defs])]), 47 "\n", 48 gpb_codegen:format_fn( 49 get_group_names, fun() -> '<Names>' end, 50 [replace_term('<Names>', [Name || {{group,Name}, _} <- Defs])]), 51 "\n", 52 gpb_codegen:format_fn( 53 get_msg_or_group_names, fun() -> '<Names>' end, 54 [replace_term('<Names>', gpb_lib:msg_or_group_names(Defs))]), 55 "\n", 56 gpb_codegen:format_fn( 57 get_enum_names, fun() -> '<Names>' end, 58 [replace_term('<Names>', [EnumName || {{enum,EnumName}, _} <- Defs])]), 59 "\n", 60 format_fetch_msg_defs(gpb_lib:msgs_or_groups(Defs)), 61 ?f("~n"), 62 format_fetch_enum_defs(EnumDefs), 63 ?f("~n"), 64 format_find_msg_defs(Defs, Opts), 65 ?f("~n"), 66 format_find_enum_defs(EnumDefs), 67 ?f("~n"), 68 format_enum_value_symbol_converters(EnumDefs), 69 ?f("~n"), 70 format_get_service_names(ServiceDefs), 71 ?f("~n"), 72 format_get_service_defs(ServiceDefs, Opts), 73 ?f("~n"), 74 format_get_rpc_names(ServiceDefs), 75 ?f("~n"), 76 format_find_rpc_defs(ServiceDefs), 77 ?f("~n"), 78 [format_find_service_rpc_defs(ServiceName, Rpcs, Opts) || {{service, ServiceName}, Rpcs} <- ServiceDefs], 79 ?f("~n"), 80 format_fetch_rpc_defs(ServiceDefs, Opts), 81 ?f("~n"), 82 format_get_package_name(Defs) 83 ]. 84 85msg_def_trees(EnumDefs, MsgDefs, GroupDefs, Opts) -> 86 EnumDefTrees = [erl_parse:abstract(EnumDef) || EnumDef <- EnumDefs], 87 MsgDefTrees = [msg_def_tree(MsgDef, Opts) || MsgDef <- MsgDefs], 88 GroupDefTrees = [group_def_tree(GroupDef, Opts) || GroupDef <- GroupDefs], 89 erl_syntax:list(EnumDefTrees ++ MsgDefTrees ++ GroupDefTrees). 90 91msg_def_tree({{msg, MsgName}, Fields}, Opts) -> 92 erl_syntax:tuple( 93 [erl_syntax:tuple([erl_syntax:atom(msg), erl_syntax:atom(MsgName)]), 94 fields_tree(Fields, Opts)]). 95 96group_def_tree({{group, Name}, Fields}, Opts) -> 97 erl_syntax:tuple( 98 [erl_syntax:tuple([erl_syntax:atom(group), erl_syntax:atom(Name)]), 99 fields_tree(Fields, Opts)]). 100 101fields_tree(Fields, Opts) -> 102 case gpb_lib:get_field_format_by_opts(Opts) of 103 fields_as_records -> 104 erl_syntax:list([field_tree(Field, Opts) || Field <- Fields]); 105 fields_as_maps -> 106 erl_syntax:list([field_tree(Field, Opts) || Field <- Fields]); 107 fields_as_proplists -> 108 erl_parse:abstract(gpb:field_records_to_proplists(Fields)) 109 end. 110 111field_tree(#?gpb_field{}=F, Opts) -> 112 [?gpb_field | FValues] = tuple_to_list(F), 113 FNames = record_info(fields, ?gpb_field), 114 gpb_lib:mapping_create( 115 ?gpb_field, 116 lists:zip(FNames, 117 [erl_parse:abstract(FValue) || FValue <- FValues]), 118 gpb_lib:mk_get_defs_as_maps_or_records_fn(Opts), 119 Opts); 120field_tree(#gpb_oneof{fields=OFields}=F, Opts) -> 121 [gpb_oneof | FValues] = tuple_to_list(F), 122 FNames = record_info(fields, gpb_oneof), 123 gpb_lib:mapping_create( 124 gpb_oneof, 125 [if FName == fields -> {FName, fields_tree(OFields, Opts)}; 126 FName /= fields -> {FName, erl_parse:abstract(FValue)} 127 end 128 || {FName, FValue} <- lists:zip(FNames, FValues)], 129 gpb_lib:mk_get_defs_as_maps_or_records_fn(Opts), 130 Opts). 131 132format_fetch_msg_defs([]) -> 133 ["-spec fetch_msg_def(_) -> no_return().\n", 134 gpb_codegen:format_fn( 135 fetch_msg_def, 136 fun(MsgName) -> erlang:error({no_such_msg, MsgName}) end)]; 137format_fetch_msg_defs(_MsgDefs) -> 138 gpb_codegen:format_fn( 139 fetch_msg_def, 140 fun(MsgName) -> 141 case find_msg_def(MsgName) of 142 Fs when is_list(Fs) -> Fs; 143 error -> erlang:error({no_such_msg, MsgName}) 144 end 145 end). 146 147format_fetch_enum_defs([]) -> 148 ["-spec fetch_enum_def(_) -> no_return().\n", 149 gpb_codegen:format_fn( 150 fetch_enum_def, 151 fun(EnumName) -> erlang:error({no_such_enum, EnumName}) end)]; 152format_fetch_enum_defs(_EnumDefs) -> 153 gpb_codegen:format_fn( 154 fetch_enum_def, 155 fun(EnumName) -> 156 case find_enum_def(EnumName) of 157 Es when is_list(Es) -> Es; 158 error -> erlang:error({no_such_enum, EnumName}) 159 end 160 end). 161 162format_find_msg_defs(Defs, Opts) -> 163 gpb_codegen:format_fn( 164 find_msg_def, 165 fun('<Name>') -> '<Fields>'; 166 (_) -> error 167 end, 168 [repeat_clauses('<Name>', 169 [[replace_term('<Name>', Name), 170 replace_tree('<Fields>', fields_tree(Fields, Opts))] 171 || {_, Name, Fields} <- gpb_lib:msgs_or_groups(Defs)])]). 172 173format_find_enum_defs(Enums) -> 174 gpb_codegen:format_fn( 175 find_enum_def, 176 fun('<EnumName>') -> '<Values>'; 177 (_) -> error 178 end, 179 [repeat_clauses('<EnumName>', 180 [[replace_term('<EnumName>', EnumName), 181 replace_term('<Values>', Values)] 182 || {{enum, EnumName}, Values} <- Enums])]). 183 184 185format_enum_value_symbol_converter_exports(Defs) -> 186 [?f("-export([enum_symbol_by_value/2, enum_value_by_symbol/2]).~n"), 187 [begin 188 ToSymFnName = gpb_lib:mk_fn(enum_symbol_by_value_, EnumName), 189 ToValFnName = gpb_lib:mk_fn(enum_value_by_symbol_, EnumName), 190 ?f("-export([~p/1, ~p/1]).~n", [ToSymFnName, ToValFnName]) 191 end 192 || {{enum, EnumName}, _EnumDef} <- Defs]]. 193 194format_enum_value_symbol_converters(EnumDefs) when EnumDefs /= [] -> 195 %% A difference between this function and `d_enum_X' as generated 196 %% by `format_enum_decoders' is that this function generates 197 %% value/symbol converters for all enums, not only for the ones 198 %% that are used in messages. 199 [gpb_codegen:format_fn( 200 enum_symbol_by_value, 201 fun('<EnumName>', Value) -> 'cvt'(Value) end, 202 [repeat_clauses( 203 '<EnumName>', 204 [[replace_term('<EnumName>', EnumName), 205 replace_term('cvt', gpb_lib:mk_fn(enum_symbol_by_value_, EnumName))] 206 || {{enum, EnumName}, _EnumDef} <- EnumDefs])]), 207 "\n", 208 gpb_codegen:format_fn( 209 enum_value_by_symbol, 210 fun('<EnumName>', Sym) -> 'cvt'(Sym) end, 211 [repeat_clauses( 212 '<EnumName>', 213 [[replace_term('<EnumName>', EnumName), 214 replace_term('cvt', gpb_lib:mk_fn(enum_value_by_symbol_, EnumName))] 215 || {{enum, EnumName}, _EnumDef} <- EnumDefs])]), 216 "\n", 217 [[gpb_codegen:format_fn( 218 gpb_lib:mk_fn(enum_symbol_by_value_, EnumName), 219 fun('<Value>') -> '<Sym>' end, 220 [repeat_clauses('<Value>', 221 [[replace_term('<Value>', EnumValue), 222 replace_term('<Sym>', EnumSym)] 223 || {EnumSym, EnumValue} <- gpb_lib:unalias_enum( 224 EnumDef)])]), 225 "\n", 226 gpb_codegen:format_fn( 227 gpb_lib:mk_fn(enum_value_by_symbol_, EnumName), 228 fun('<Sym>') -> '<Value>' end, 229 [repeat_clauses('<Sym>', 230 [[replace_term('<Value>', EnumValue), 231 replace_term('<Sym>', EnumSym)] 232 || {EnumSym, EnumValue} <- EnumDef])])] 233 || {{enum, EnumName}, EnumDef} <- EnumDefs]]; 234format_enum_value_symbol_converters([]=_EnumDefs) -> 235 ["-spec enum_symbol_by_value(_, _) -> no_return().\n", 236 gpb_codegen:format_fn( 237 enum_symbol_by_value, 238 fun(E, V) -> erlang:error({no_enum_defs, E, V}) end), 239 "\n", 240 "-spec enum_value_by_symbol(_, _) -> no_return().\n", 241 gpb_codegen:format_fn( 242 enum_value_by_symbol, 243 fun(E, V) -> erlang:error({no_enum_defs, E, V}) end), 244 "\n"]. 245 246format_get_package_name(Defs) -> 247 case lists:keyfind(package, 1, Defs) of 248 false -> 249 gpb_codegen:format_fn( 250 get_package_name, fun() -> undefined end); 251 {package, Package} -> 252 gpb_codegen:format_fn( 253 get_package_name, fun() -> '<Package>' end, 254 [replace_term('<Package>', Package)]) 255 end. 256 257% --- service introspection methods 258 259format_get_service_names(ServiceDefs) -> 260 gpb_codegen:format_fn( 261 get_service_names, 262 fun() -> '<ServiceNames>' end, 263 [replace_term( 264 '<ServiceNames>', 265 [ServiceName || {{service, ServiceName}, _Rpcs} <- ServiceDefs])]). 266 267format_get_service_defs(ServiceDefs, Opts) -> 268 gpb_codegen:format_fn( 269 get_service_def, 270 fun('<ServiceName>') -> '<ServiceDef>'; 271 (_) -> error 272 end, 273 [repeat_clauses( 274 '<ServiceName>', 275 [[replace_term('<ServiceName>', ServiceName), 276 replace_tree('<ServiceDef>', service_def_tree(ServiceDef, Opts))] 277 || {{service, ServiceName}, _Rpcs} = ServiceDef <- ServiceDefs])]). 278 279format_get_rpc_names(ServiceDefs) -> 280 gpb_codegen:format_fn( 281 get_rpc_names, 282 fun('<ServiceName>') -> '<ServiceRpcNames>'; 283 (_) -> error 284 end, 285 [repeat_clauses('<ServiceName>', 286 [[replace_term('<ServiceName>', ServiceName), 287 replace_term('<ServiceRpcNames>', 288 [RpcName 289 || #?gpb_rpc{name=RpcName} <- Rpcs])] 290 || {{service, ServiceName}, Rpcs} <- ServiceDefs])]). 291 292format_find_rpc_defs(ServiceDefs) -> 293 gpb_codegen:format_fn( 294 find_rpc_def, 295 fun('<ServiceName>', RpcName) -> '<ServiceFindRpcDef>'(RpcName); 296 (_, _) -> error 297 end, 298 [repeat_clauses( 299 '<ServiceName>', 300 [[replace_term('<ServiceName>', ServiceName), 301 replace_term('<ServiceFindRpcDef>', 302 gpb_lib:mk_fn(find_rpc_def_, ServiceName))] 303 || {{service, ServiceName}, _} <- ServiceDefs])]). 304 305format_find_service_rpc_defs(ServiceName, Rpcs, Opts) -> 306 gpb_codegen:format_fn( 307 gpb_lib:mk_fn(find_rpc_def_, ServiceName), 308 fun('<RpcName>') -> '<RpcDef>'; 309 (_) -> error 310 end, 311 [repeat_clauses('<RpcName>', 312 [[replace_term('<RpcName>', RpcName), 313 replace_tree('<RpcDef>', rpc_def_tree(Rpc, Opts))] 314 || #?gpb_rpc{name=RpcName} = Rpc <- Rpcs])]). 315 316format_fetch_rpc_defs([], _Opts) -> 317 ["-spec fetch_rpc_def(_, _) -> no_return().\n", 318 gpb_codegen:format_fn( 319 fetch_rpc_def, 320 fun(ServiceName, RpcName) -> 321 erlang:error({no_such_rpc, ServiceName, RpcName}) 322 end)]; 323format_fetch_rpc_defs(_ServiceDefs, Opts) -> 324 gpb_codegen:format_fn( 325 fetch_rpc_def, 326 fun(ServiceName, RpcName) -> 327 case find_rpc_def(ServiceName, RpcName) of 328 Def when is_X(Def) -> Def; 329 error -> erlang:error({no_such_rpc, ServiceName, RpcName}) 330 end 331 end, 332 [replace_term(is_X, 333 case get_rpc_format_by_opts(Opts) of 334 rpcs_as_proplists -> 335 is_list; 336 rpcs_as_records -> 337 case gpb_lib:get_records_or_maps_by_opts(Opts) of 338 maps -> is_map; 339 records -> is_tuple 340 end 341 end)]). 342 343service_def_tree({{service, ServiceName}, Rpcs}, Opts) -> 344 erl_syntax:tuple( 345 [erl_syntax:tuple([erl_syntax:atom(service), 346 erl_syntax:atom(ServiceName)]), 347 rpcs_def_tree(Rpcs, Opts)]). 348 349get_rpc_format_by_opts(Opts) -> 350 case proplists:get_bool(defs_as_proplists, proplists:unfold(Opts)) of 351 false -> rpcs_as_records; %% default 352 true -> rpcs_as_proplists 353 end. 354 355rpc_record_def_tree(#?gpb_rpc{}=Rpc, Opts) -> 356 [?gpb_rpc | RValues] = tuple_to_list(Rpc), 357 RNames = record_info(fields, ?gpb_rpc), 358 gpb_lib:mapping_create( 359 ?gpb_rpc, 360 lists:zip(RNames, 361 [erl_parse:abstract(RValue) || RValue <- RValues]), 362 gpb_lib:mk_get_defs_as_maps_or_records_fn(Opts), 363 Opts). 364 365rpcs_def_tree(Rpcs, Opts) -> 366 case get_rpc_format_by_opts(Opts) of 367 rpcs_as_records -> 368 erl_syntax:list([rpc_record_def_tree(Rpc, Opts) || Rpc <- Rpcs]); 369 rpcs_as_proplists -> 370 erl_parse:abstract(gpb:rpc_records_to_proplists(Rpcs)) 371 end. 372 373rpc_def_tree(#?gpb_rpc{}=Rpc, Opts) -> 374 case get_rpc_format_by_opts(Opts) of 375 rpcs_as_records -> 376 rpc_record_def_tree(Rpc, Opts); 377 rpcs_as_proplists -> 378 erl_parse:abstract(gpb:rpc_record_to_proplist(Rpc)) 379 end. 380 381