11% %% @doc APIs to work with Avro IDL format
22% %%
3- % %% See [https://avro.apache.org/docs/1.9.2/idl.html]
3+ % %% This module allows to convert .avdl format to .avpr and .avsc as well
4+ % %% as create Avro encoders and decoders.
5+ % %% @end
6+ % %% @reference See [https://avro.apache.org/docs/current/idl.html]
7+ % %% @author Sergey Prokhhorov <me@seriyps.ru>
48-module (avro_idl ).
59
610-export ([new_context /1 ,
711 str_to_avpr /2 ,
812 protocol_to_avpr /2 ,
913 typedecl_to_avsc /2 ]).
1014-include (" idl.hrl" ).
15+ -include (" erlavro.hrl" ).
1116
1217-record (st , {cwd }).
1318
@@ -32,13 +37,13 @@ protocol_to_avpr(#protocol{name = Name,
3237 (_ ) -> true
3338 end , Defs ),
3439 Protocol0 =
35- #{protocol => Name ,
36- types =>
40+ #{<< " protocol" >> => b ( Name ) ,
41+ << " types" >> =>
3742 lists :map (
3843 fun (Type ) ->
3944 typedecl_to_avsc (Type , St )
4045 end , Types ),
41- messages =>
46+ << " messages" >> =>
4247 lists :map (
4348 fun (Message ) ->
4449 message_to_avsc (Message , St )
@@ -57,36 +62,36 @@ process_imports(Defs, _St) ->
5762
5863typedecl_to_avsc (# enum {name = Name , meta = Meta , variants = Vars }, _St ) ->
5964 meta (
60- #{type => enum ,
61- name => Name ,
62- variants => Vars
65+ #{<< " type" >> => ? AVRO_ENUM ,
66+ << " name" >> => b ( Name ) ,
67+ << " variants" >> => lists : map ( fun b / 1 , Vars )
6368 },
6469 Meta );
6570typedecl_to_avsc (# fixed {name = Name , meta = Meta , size = Size }, _St ) ->
6671 meta (
67- #{type => fixed ,
68- name => Name ,
69- size => Size },
72+ #{<< " type" >> => ? AVRO_FIXED ,
73+ << " name" >> => b ( Name ) ,
74+ << " size" >> => Size },
7075 Meta );
7176typedecl_to_avsc (# error {name = Name , meta = Meta , fields = Fields }, St ) ->
7277 meta (
73- #{type => error ,
74- name => Name ,
75- fields => [field_to_avsc (Field , St ) || Field <- Fields ]},
78+ #{<< " type" >> => ? AVRO_ERROR ,
79+ << " name" >> => b ( Name ) ,
80+ << " fields" >> => [field_to_avsc (Field , St ) || Field <- Fields ]},
7681 Meta );
7782typedecl_to_avsc (# record {name = Name , meta = Meta , fields = Fields }, St ) ->
7883 meta (
79- #{type => record ,
80- name => Name ,
81- fields => [field_to_avsc (Field , St ) || Field <- Fields ]},
84+ #{<< " type" >> => ? AVRO_RECORD ,
85+ << " name" >> => b ( Name ) ,
86+ << " fields" >> => [field_to_avsc (Field , St ) || Field <- Fields ]},
8287 Meta ).
8388
8489field_to_avsc (# field {name = Name , meta = Meta ,
8590 type = Type , default = Default }, St ) ->
8691 meta (
8792 default (
88- #{name => Name ,
89- type => type_to_avsc (Type , St )},
93+ #{<< " name" >> => b ( Name ) ,
94+ << " type" >> => type_to_avsc (Type , St )},
9095 Default ), % TODO: maybe validate default matches type
9196 Meta ).
9297
@@ -96,62 +101,60 @@ message_to_avsc(#function{name = Name, meta = Meta,
96101 % % TODO: arguments can just reuse `#field{}`
97102 ArgsSchema =
98103 [default (
99- #{name => ArgName ,
100- type => type_to_avsc (Type , St )},
104+ #{<< " name" >> => b ( ArgName ) ,
105+ << " type" >> => type_to_avsc (Type , St )},
101106 Default )
102107 || {arg , ArgName , Type , Default } <- Args ],
103108 Schema0 =
104- #{name => Name ,
105- request => ArgsSchema ,
106- response => type_to_avsc (Return , St )},
109+ #{<< " name" >> => b ( Name ) ,
110+ << " request" >> => ArgsSchema ,
111+ << " response" >> => type_to_avsc (Return , St )},
107112 Schema1 = case Extra of
108113 undefined -> Schema0 ;
109114 oneway ->
110- Schema0 #{' one-way' => true };
115+ Schema0 #{<< " one-way" >> => true };
111116 {throws , ThrowsTypes } ->
112- % % Throws = [type_to_avsc(TType, St)
113- % % || TType <- ThrowsTypes],
114- Schema0 #{error => ThrowsTypes }
117+ Schema0 #{<<" error" >> => lists :map (fun b /1 , ThrowsTypes )}
115118 end ,
116119 meta (Schema1 , Meta ).
117120
118121
119122type_to_avsc (void , _St ) ->
120- null ;
123+ ? AVRO_NULL ;
121124type_to_avsc (null , _St ) ->
122- null ;
125+ ? AVRO_NULL ;
123126type_to_avsc (T , _St ) when T == int ;
124127 T == long ;
125128 T == string ;
126129 T == boolean ;
127130 T == float ;
128131 T == double ;
129132 T == bytes ->
130- T ;
133+ atom_to_binary ( T , utf8 ) ;
131134type_to_avsc ({decimal , Precision , Scale }, _St ) ->
132- #{type => bytes ,
133- ' logicalType' => " decimal" ,
134- precision => Precision ,
135- scale => Scale };
135+ #{<< " type" >> => ? AVRO_BYTES ,
136+ << " logicalType" >> => << " decimal" >> ,
137+ << " precision" >> => Precision ,
138+ << " scale" >> => Scale };
136139type_to_avsc (date , _St ) ->
137- #{type => int ,
138- ' logicalType' => " date" };
140+ #{<< " type" >> => ? AVRO_INT ,
141+ << " logicalType" >> => << " date" >> };
139142type_to_avsc (time_ms , _St ) ->
140- #{type => int ,
141- ' logicalType' => " time-millis" };
143+ #{<< " type" >> => ? AVRO_INT ,
144+ << " logicalType" >> => << " time-millis" >> };
142145type_to_avsc (timestamp_ms , _St ) ->
143- #{type => long ,
144- ' logicalType' => " timestamp-millis" };
146+ #{<< " type" >> => ? AVRO_LONG ,
147+ << " logicalType" >> => << " timestamp-millis" >> };
145148type_to_avsc ({custom , Id }, _St ) ->
146- Id ;
149+ b ( Id ) ;
147150type_to_avsc ({union , Types }, St ) ->
148151 [type_to_avsc (Type , St ) || Type <- Types ];
149152type_to_avsc ({array , Of }, St ) ->
150- #{type => array ,
151- items => type_to_avsc (Of , St )};
153+ #{<< " type" >> => ? AVRO_ARRAY ,
154+ << " items" >> => type_to_avsc (Of , St )};
152155type_to_avsc ({map , ValType }, St ) ->
153- #{type => map ,
154- values => type_to_avsc (ValType , St )}.
156+ #{<< " type" >> => ? AVRO_MAP ,
157+ << " values" >> => type_to_avsc (ValType , St )}.
155158
156159meta (Schema , Meta ) ->
157160 {Docs , Annotations } =
@@ -163,17 +166,27 @@ meta(Schema, Meta) ->
163166 [] -> Schema ;
164167 _ ->
165168 DocStrings = [S || {doc , S } <- Docs ],
166- Schema #{" doc" => lists : flatten (lists :join (
167- " \n " , DocStrings ))}
169+ Schema #{<< " doc" >> => b (lists :join (
170+ " \n " , DocStrings ))}
168171 end ,
169172 lists :foldl (
170173 fun (# annotation {name = Name , value = Value }, Schema2 ) ->
171- maps :is_key (Name , Schema2 ) andalso
174+ BName = b (Name ),
175+ BVal = case Value of
176+ [] -> <<>>;
177+ [C | _ ] when is_integer (C ) -> b (Value );
178+ _ ->
179+ [b (Str ) || Str <- Value ]
180+ end ,
181+ maps :is_key (BName , Schema2 ) andalso
172182 error ({duplicate_annotation , Name , Value , Schema2 }),
173- Schema2 #{Name => Value }
183+ Schema2 #{BName => BVal }
174184 end , Schema1 , Annotations ).
175185
176186default (Obj , undefined ) ->
177187 Obj ;
178188default (Obj , Default ) ->
179- Obj #{default => Default }.
189+ Obj #{<<" default" >> => Default }.
190+
191+ b (Str ) when is_list (Str ) ->
192+ unicode :characters_to_binary (Str ).
0 commit comments