• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
No new info detected.

ahamez / protox / a5fe55d387dd729053b1a70128602438cb292f5f

18 Jan 2025 04:46PM UTC coverage: 91.379% (-1.5%) from 92.91%
a5fe55d387dd729053b1a70128602438cb292f5f

push

github

ahamez
refactor!: remove field_def/1 and store message fields as a map

Functionality still available through schema/1

18 of 18 new or added lines in 4 files covered. (100.0%)

20 existing lines in 4 files now uncovered.

742 of 812 relevant lines covered (91.38%)

18368.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.82
/lib/protox/define_decoder.ex
1
defmodule Protox.DefineDecoder do
2
  @moduledoc false
3
  # Internal. Generates the decoder of a message.
4

5
  @default_keep_unknown_field true
6

7
  alias Protox.Field
8

9
  use Protox.{
10
    Float,
11
    WireTypes
12
  }
13

14
  def define(msg_name, fields, required_fields, opts \\ []) do
15
    vars = %{
80✔
16
      bytes: Macro.var(:bytes, __MODULE__),
17
      delimited: Macro.var(:delimited, __MODULE__),
18
      field: Macro.var(:field, __MODULE__),
19
      msg: Macro.var(:msg, __MODULE__),
20
      rest: Macro.var(:rest, __MODULE__),
21
      set_fields: Macro.var(:set_fields, __MODULE__),
22
      value: Macro.var(:value, __MODULE__)
23
    }
24

25
    # The public function to decode the binary protobuf.
26
    decode_fun = make_decode_fun(required_fields, msg_name, vars)
80✔
27

28
    # The function that decodes the binary protobuf and possibly dispatches to other decoding
29
    # functions.
30
    parse_key_value_fun = make_parse_key_value_fun(required_fields, fields, vars, opts)
80✔
31

32
    # The functions that decodes maps.
33
    parse_map_entries = make_parse_map_entries_funs(vars, fields)
80✔
34

35
    quote do
36
      unquote(decode_fun)
37
      unquote(parse_key_value_fun)
38
      unquote(parse_map_entries)
39
    end
40
  end
41

42
  defp make_decode_fun(required_fields, msg_name, vars) do
43
    decode_bang_fun = make_decode_bang_fun(required_fields, msg_name, vars)
80✔
44

45
    quote do
46
      @spec decode(binary()) :: {:ok, struct()} | {:error, any()}
47
      def decode(bytes) do
48
        try do
49
          {:ok, decode!(bytes)}
50
        rescue
51
          e in [Protox.DecodingError, Protox.IllegalTagError, Protox.RequiredFieldsError] ->
52
            {:error, e}
53
        end
54
      end
55

56
      unquote(decode_bang_fun)
57
    end
58
  end
59

60
  defp make_decode_bang_fun([], msg_name, _vars) do
61
    quote do
62
      @spec decode!(binary()) :: struct() | no_return()
63
      def decode!(bytes) do
64
        parse_key_value(bytes, struct(unquote(msg_name)))
65
      end
66
    end
67
  end
68

69
  defp make_decode_bang_fun(required_fields, msg_name, vars) do
70
    quote do
71
      @spec decode!(binary()) :: struct() | no_return()
72
      def decode!(bytes) do
UNCOV
73
        {msg, unquote(vars.set_fields)} = parse_key_value([], bytes, struct(unquote(msg_name)))
×
74

UNCOV
75
        case unquote(required_fields) -- unquote(vars.set_fields) do
×
76
          [] -> msg
77
          missing_fields -> raise Protox.RequiredFieldsError.new(missing_fields)
78
        end
79
      end
80
    end
81
  end
82

83
  defp make_parse_key_value_fun(required_fields, fields, vars, opts) do
84
    keep_set_fields = required_fields != []
80✔
85

86
    parse_key_value_body =
80✔
87
      make_parse_key_value_body(keep_set_fields, fields, vars, opts)
88

89
    if keep_set_fields do
80✔
90
      quote do
91
        @spec parse_key_value([atom()], binary(), struct()) :: {struct(), [atom()]}
UNCOV
92
        defp parse_key_value(unquote(vars.set_fields), <<>>, msg) do
×
UNCOV
93
          {msg, unquote(vars.set_fields)}
×
94
        end
95

UNCOV
96
        defp parse_key_value(unquote(vars.set_fields), bytes, msg) do
×
97
          unquote(parse_key_value_body)
98
        end
99
      end
100
    else
101
      quote do
102
        @spec parse_key_value(binary(), struct()) :: struct()
103
        defp parse_key_value(<<>>, msg), do: msg
104

105
        defp parse_key_value(bytes, msg), do: unquote(parse_key_value_body)
106
      end
107
    end
108
  end
109

110
  defp make_parse_key_value_body(keep_set_fields, fields, vars, opts) do
111
    # Fragment to handle the (invalid) field with tag 0.
112
    tag_0_case = make_parse_key_value_tag_0()
80✔
113

114
    # Fragment to parse unknown fields. Those are identified with an unknown tag.
115
    keep_unknown_fields = Keyword.get(opts, :keep_unknown_fields, @default_keep_unknown_field)
80✔
116
    unknown_fields_name = Keyword.fetch!(opts, :unknown_fields_name)
80✔
117

118
    unknown_tag_case =
80✔
119
      make_parse_key_value_unknown(
120
        vars,
121
        keep_set_fields,
122
        keep_unknown_fields,
123
        unknown_fields_name
124
      )
125

126
    # Fragment to parse known fields.
127
    known_tags_case = make_parse_key_value_known(vars, fields, keep_set_fields)
80✔
128

129
    all_cases = tag_0_case ++ known_tags_case ++ unknown_tag_case
80✔
130

131
    if keep_set_fields do
80✔
132
      quote do
UNCOV
133
        {new_set_fields, unquote(vars.field), rest} =
×
134
          case Protox.Decode.parse_key(bytes) do
135
            unquote(all_cases)
136
          end
137

UNCOV
138
        msg_updated = struct(unquote(vars.msg), unquote(vars.field))
×
139
        parse_key_value(new_set_fields, rest, msg_updated)
140
      end
141
    else
142
      quote do
143
        {unquote(vars.field), rest} =
80✔
144
          case Protox.Decode.parse_key(bytes) do
145
            unquote(all_cases)
146
          end
147

148
        msg_updated = struct(unquote(vars.msg), unquote(vars.field))
80✔
149
        parse_key_value(rest, msg_updated)
150
      end
151
    end
152
  end
153

154
  defp make_parse_key_value_tag_0() do
80✔
155
    quote do
156
      {0, _, _} -> raise %Protox.IllegalTagError{}
157
    end
158
  end
159

160
  defp make_parse_key_value_known(vars, fields, keep_set_fields) do
161
    Enum.flat_map(fields, fn %Field{} = field ->
80✔
162
      single = make_single_case(vars, keep_set_fields, field)
935✔
163

164
      single_generated = single != []
935✔
165
      delimited = make_delimited_case(vars, keep_set_fields, single_generated, field)
935✔
166

167
      delimited ++ single
935✔
168
    end)
169
  end
170

171
  defp make_parse_key_value_unknown(
172
         vars,
173
         keep_set_fields,
174
         true = _keep_unknown_fields,
175
         unknown_fields_name
176
       ) do
177
    body =
80✔
178
      quote do
179
        {
180
          unquote(unknown_fields_name),
181
          # Order is important here, we want to keep the order of the unknown fields.
182
          unquote(vars.msg).unquote(unknown_fields_name) ++ [unquote(vars.value)]
80✔
183
        }
184
      end
185

186
    case_return =
80✔
187
      case keep_set_fields do
UNCOV
188
        true -> quote(do: {unquote(vars.set_fields), [unquote(body)], rest})
×
189
        # No need to maintain a list of set fields when the list of required fields is empty
190
        false -> quote(do: {[unquote(body)], rest})
80✔
191
      end
192

193
    quote do
194
      {tag, wire_type, rest} ->
195
        {unquote(vars.value), rest} = Protox.Decode.parse_unknown(tag, wire_type, rest)
80✔
196

197
        unquote(case_return)
198
    end
199
  end
200

201
  defp make_parse_key_value_unknown(
202
         vars,
203
         keep_set_fields,
204
         false = _keep_unknown_fields,
205
         _unknown_fields_name
206
       ) do
207
    # No need to maintain a list of set fields when the list of required fields is empty
208
    case_return =
×
209
      case keep_set_fields do
210
        true -> quote do: {unquote(vars.set_fields), [], rest}
×
211
        false -> quote do: {[], rest}
×
212
      end
213

214
    quote do
215
      {tag, wire_type, rest} ->
216
        {_, rest} = Protox.Decode.parse_unknown(tag, wire_type, rest)
217

218
        unquote(case_return)
219
    end
220
  end
221

222
  defp make_single_case(_vars, _keep_set_fields, %Field{type: {:message, _}}) do
225✔
223
    quote(do: [])
224
  end
225

226
  defp make_single_case(_vars, _keep_set_fields, %Field{type: :string}), do: quote(do: [])
100✔
227
  defp make_single_case(_vars, _keep_set_fields, %Field{type: :bytes}), do: quote(do: [])
20✔
228

229
  defp make_single_case(_vars, _keep_set_fields, %Field{type: {x, _}}) when x != :enum do
95✔
230
    quote(do: [])
231
  end
232

233
  defp make_single_case(vars, keep_set_fields, %Field{} = field) do
234
    parse_single = make_parse_single(vars.bytes, field.type)
495✔
235
    update_field = make_update_field(vars.value, field, vars, _wrap_value = true)
495✔
236

237
    # No need to maintain a list of set fields for proto3
238
    case_return =
495✔
239
      case keep_set_fields do
240
        true ->
UNCOV
241
          quote do: {[unquote(field.name) | set_fields], [unquote(update_field)], rest}
×
242

243
        false ->
495✔
244
          quote do: {[unquote(update_field)], rest}
245
      end
246

247
    quote do
248
      {unquote(field.tag), _, unquote(vars.bytes)} ->
495✔
249
        {value, rest} = unquote(parse_single)
250
        unquote(case_return)
251
    end
252
  end
253

254
  defp make_delimited_case(
255
         vars,
256
         keep_set_fields,
257
         single_generated,
258
         %Field{type: {:message, _}} = field
259
       ) do
260
    make_delimited_case_impl(vars, keep_set_fields, single_generated, field)
225✔
261
  end
262

263
  defp make_delimited_case(vars, keep_set_fields, single_generated, %Field{type: :bytes} = field) do
264
    make_delimited_case_impl(vars, keep_set_fields, single_generated, field)
20✔
265
  end
266

267
  defp make_delimited_case(vars, keep_set_fields, single_generated, %Field{type: :string} = field) do
268
    make_delimited_case_impl(vars, keep_set_fields, single_generated, field)
100✔
269
  end
270

271
  defp make_delimited_case(_vars, _keep_set_fields, _single_generated, %Field{kind: {:scalar, _}}) do
245✔
272
    []
273
  end
274

275
  defp make_delimited_case(_vars, _keep_set_fields, _single_generated, %Field{kind: {:oneof, _}}) do
35✔
276
    []
277
  end
278

279
  defp make_delimited_case(vars, keep_set_fields, single_generated, %Field{} = field) do
280
    make_delimited_case_impl(vars, keep_set_fields, single_generated, field)
310✔
281
  end
282

283
  defp make_delimited_case_impl(vars, keep_set_fields, single_generated, %Field{} = field) do
284
    # If the case to decode single occurrences of repeated elements has been generated,
285
    # it means that it's a repeated field of scalar elements (as non-scalar cannot be packed,
286
    # see https://developers.google.com/protocol-buffers/docs/encoding#optional).
287
    # Thus, it's useless to wrap in a list the result of the decoding as it means
288
    # we're using a parse_repeated_* function that always returns a list.
289
    update_field =
655✔
290
      if field.type == :bytes do
655✔
291
        make_update_field(vars.delimited, field, vars, _wrap_value = !single_generated)
20✔
292
      else
293
        parse_delimited = make_parse_delimited(vars.delimited, field.type)
635✔
294
        make_update_field(parse_delimited, field, vars, _wrap_value = !single_generated)
635✔
295
      end
296

297
    case_return =
655✔
298
      case keep_set_fields do
UNCOV
299
        true -> quote do: {[unquote(field.name) | set_fields], [unquote(update_field)], rest}
×
300
        false -> quote do: {[unquote(update_field)], rest}
655✔
301
      end
302

303
    # If `single` was not generated, then we don't need the `@wire_delimited discrimant
304
    # as there is only one clause for this `tag`.
305
    wire_type =
655✔
306
      case single_generated do
307
        true -> quote do: unquote(@wire_delimited)
215✔
308
        false -> quote do: _
309
      end
310

311
    quote do
312
      {unquote(field.tag), unquote(wire_type), unquote(vars.bytes)} ->
655✔
313
        {len, unquote(vars.bytes)} = Protox.Varint.decode(unquote(vars.bytes))
655✔
314
        {unquote(vars.delimited), rest} = Protox.Decode.parse_delimited(unquote(vars.bytes), len)
655✔
315
        unquote(case_return)
316
    end
317
  end
318

319
  defp make_update_field(value, %Field{kind: :map} = field, vars, _wrap_value) do
320
    quote do
321
      {entry_key, entry_value} = unquote(value)
322

323
      {unquote(field.name),
95✔
324
       Map.put(unquote(vars.msg).unquote(field.name), entry_key, entry_value)}
95✔
325
    end
326
  end
327

328
  defp make_update_field(
329
         value,
330
         %Field{label: :proto3_optional, kind: {:oneof, _}, type: {:message, _}} = field,
331
         vars,
332
         _wrap_value
333
       ) do
334
    quote do
335
      case unquote(vars.msg).unquote(field.name) do
×
336
        {unquote(field.name), previous_value} ->
×
337
          {unquote(field.name), Protox.MergeMessage.merge(previous_value, unquote(value))}
×
338

339
        _ ->
340
          {unquote(field.name), unquote(value)}
×
341
      end
342
    end
343
  end
344

345
  defp make_update_field(
346
         value,
347
         %Field{kind: {:oneof, parent_field}, type: {:message, _}} = field,
348
         vars,
349
         _wrap_value
350
       ) do
351
    quote do
352
      case unquote(vars.msg).unquote(parent_field) do
5✔
353
        {unquote(field.name), previous_value} ->
5✔
354
          {unquote(parent_field),
355
           {unquote(field.name), Protox.MergeMessage.merge(previous_value, unquote(value))}}
5✔
356

357
        _ ->
358
          {unquote(parent_field), {unquote(field.name), unquote(value)}}
5✔
359
      end
360
    end
361
  end
362

363
  defp make_update_field(value, %Field{kind: {:oneof, parent_field}} = field, _vars, _wrap_value) do
364
    case field.label do
45✔
365
      :proto3_optional ->
×
366
        quote(do: {unquote(field.name), unquote(value)})
×
367

368
      _ ->
45✔
369
        quote(do: {unquote(parent_field), {unquote(field.name), unquote(value)}})
45✔
370
    end
371
  end
372

373
  defp make_update_field(
120✔
374
         value,
375
         %Field{kind: {:scalar, _}, type: {:message, _}} = field,
376
         vars,
377
         _wrap_value
378
       ) do
379
    quote do
380
      {
381
        unquote(field.name),
120✔
382
        Protox.MergeMessage.merge(unquote(vars.msg).unquote(field.name), unquote(value))
120✔
383
      }
384
    end
385
  end
386

387
  defp make_update_field(value, %Field{kind: {:scalar, _}} = field, _vars, _wrap_value) do
335✔
388
    quote(do: {unquote(field.name), unquote(value)})
335✔
389
  end
390

391
  defp make_update_field(value, %Field{} = field, vars, true = _wrap_value) do
335✔
392
    quote do
393
      {unquote(field.name), unquote(vars.msg).unquote(field.name) ++ [unquote(value)]}
335✔
394
    end
395
  end
396

397
  defp make_update_field(value, %Field{} = field, vars, false = _wrap_value) do
215✔
398
    quote do
399
      {unquote(field.name), unquote(vars.msg).unquote(field.name) ++ unquote(value)}
215✔
400
    end
401
  end
402

403
  defp make_parse_delimited(bytes_var, :bytes) do
404
    quote(do: unquote(bytes_var))
5✔
405
  end
406

407
  defp make_parse_delimited(bytes_var, :string) do
408
    quote(do: Protox.Decode.validate_string!(unquote(bytes_var)))
409
  end
410

411
  defp make_parse_delimited(bytes_var, {:enum, mod}) do
412
    quote(do: Protox.Decode.parse_repeated_enum([], unquote(bytes_var), unquote(mod)))
413
  end
414

415
  defp make_parse_delimited(bytes_var, {:message, mod}) do
416
    quote(do: unquote(mod).decode!(unquote(bytes_var)))
417
  end
418

419
  defp make_parse_delimited(bytes_var, :bool) do
420
    quote(do: Protox.Decode.parse_repeated_bool([], unquote(bytes_var)))
421
  end
422

423
  defp make_parse_delimited(bytes_var, :int32) do
424
    quote(do: Protox.Decode.parse_repeated_int32([], unquote(bytes_var)))
425
  end
426

427
  defp make_parse_delimited(bytes_var, :uint32) do
428
    quote(do: Protox.Decode.parse_repeated_uint32([], unquote(bytes_var)))
429
  end
430

431
  defp make_parse_delimited(bytes_var, :sint32) do
432
    quote(do: Protox.Decode.parse_repeated_sint32([], unquote(bytes_var)))
433
  end
434

435
  defp make_parse_delimited(bytes_var, :int64) do
436
    quote(do: Protox.Decode.parse_repeated_int64([], unquote(bytes_var)))
437
  end
438

439
  defp make_parse_delimited(bytes_var, :uint64) do
440
    quote(do: Protox.Decode.parse_repeated_uint64([], unquote(bytes_var)))
441
  end
442

443
  defp make_parse_delimited(bytes_var, :sint64) do
444
    quote(do: Protox.Decode.parse_repeated_sint64([], unquote(bytes_var)))
445
  end
446

447
  defp make_parse_delimited(bytes_var, :fixed32) do
448
    quote(do: Protox.Decode.parse_repeated_fixed32([], unquote(bytes_var)))
449
  end
450

451
  defp make_parse_delimited(bytes_var, :fixed64) do
452
    quote(do: Protox.Decode.parse_repeated_fixed64([], unquote(bytes_var)))
453
  end
454

455
  defp make_parse_delimited(bytes_var, :sfixed32) do
456
    quote(do: Protox.Decode.parse_repeated_sfixed32([], unquote(bytes_var)))
457
  end
458

459
  defp make_parse_delimited(bytes_var, :sfixed64) do
460
    quote(do: Protox.Decode.parse_repeated_sfixed64([], unquote(bytes_var)))
461
  end
462

463
  defp make_parse_delimited(bytes_var, :float) do
464
    quote(do: Protox.Decode.parse_repeated_float([], unquote(bytes_var)))
465
  end
466

467
  defp make_parse_delimited(bytes_var, :double) do
468
    quote(do: Protox.Decode.parse_repeated_double([], unquote(bytes_var)))
469
  end
470

471
  defp make_parse_delimited(bytes_var, {key_type, value_type}) do
472
    unset_map_value =
95✔
473
      case value_type do
474
        {:message, msg_type} -> quote(do: struct(unquote(msg_type)))
475
        _ -> quote(do: Protox.Default.default(unquote(value_type)))
476
      end
477

478
    parser_fun_name = make_map_decode_fun_name(key_type, value_type)
95✔
479

480
    quote do
481
      {map_key, map_value} = unquote(parser_fun_name)({:unset, :unset}, unquote(bytes_var))
482

483
      map_key =
484
        case map_key do
485
          :unset -> Protox.Default.default(unquote(key_type))
486
          _ -> map_key
487
        end
488

489
      map_value =
490
        case map_value do
491
          :unset -> unquote(unset_map_value)
492
          _ -> map_value
493
        end
494

495
      {map_key, map_value}
496
    end
497
  end
498

499
  defp make_parse_single(bytes_var, :double) do
500
    quote(do: Protox.Decode.parse_double(unquote(bytes_var)))
501
  end
502

503
  defp make_parse_single(bytes_var, :float) do
504
    quote(do: Protox.Decode.parse_float(unquote(bytes_var)))
505
  end
506

507
  defp make_parse_single(bytes_var, :sfixed64) do
508
    quote(do: Protox.Decode.parse_sfixed64(unquote(bytes_var)))
509
  end
510

511
  defp make_parse_single(bytes_var, :fixed64) do
512
    quote(do: Protox.Decode.parse_fixed64(unquote(bytes_var)))
513
  end
514

515
  defp make_parse_single(bytes_var, :sfixed32) do
516
    quote(do: Protox.Decode.parse_sfixed32(unquote(bytes_var)))
517
  end
518

519
  defp make_parse_single(bytes_var, :fixed32) do
520
    quote(do: Protox.Decode.parse_fixed32(unquote(bytes_var)))
521
  end
522

523
  defp make_parse_single(bytes_var, :bool) do
524
    quote(do: Protox.Decode.parse_bool(unquote(bytes_var)))
525
  end
526

527
  defp make_parse_single(bytes_var, :sint32) do
528
    quote(do: Protox.Decode.parse_sint32(unquote(bytes_var)))
529
  end
530

531
  defp make_parse_single(bytes_var, :sint64) do
532
    quote(do: Protox.Decode.parse_sint64(unquote(bytes_var)))
533
  end
534

535
  defp make_parse_single(bytes_var, :uint32) do
536
    quote(do: Protox.Decode.parse_uint32(unquote(bytes_var)))
537
  end
538

539
  defp make_parse_single(bytes_var, :uint64) do
540
    quote(do: Protox.Decode.parse_uint64(unquote(bytes_var)))
541
  end
542

543
  defp make_parse_single(bytes_var, :int32) do
544
    quote(do: Protox.Decode.parse_int32(unquote(bytes_var)))
545
  end
546

547
  defp make_parse_single(bytes_var, :int64) do
548
    quote(do: Protox.Decode.parse_int64(unquote(bytes_var)))
549
  end
550

551
  defp make_parse_single(bytes_var, {:enum, mod}) do
552
    quote(do: Protox.Decode.parse_enum(unquote(bytes_var), unquote(mod)))
553
  end
554

555
  defp make_parse_map_entries_funs(vars, fields) do
556
    {maps, _other_fields} = Protox.Defs.split_maps(fields)
80✔
557

558
    maps
559
    |> Enum.map(fn %Field{kind: :map} = field ->
560
      key_type = elem(field.type, 0)
95✔
561
      value_type = elem(field.type, 1)
95✔
562

563
      fun_name = make_map_decode_fun_name(key_type, value_type)
95✔
564

565
      key_parser = make_parse_map_entry(vars, key_type)
95✔
566
      value_parser = make_parse_map_entry(vars, value_type)
95✔
567

568
      code =
95✔
569
        quote do
570
          defp unquote(fun_name)(map_entry, <<>>) do
571
            map_entry
572
          end
573

574
          # https://developers.google.com/protocol-buffers/docs/proto3#backwards-compatibility
575
          # Maps are equivalent to:
576
          #   message MapFieldEntry {
577
          #     key_type key = 1;
578
          #     value_type value = 2;
579
          #   }
580
          # repeated MapFieldEntry map_field = N;
581
          #
582
          defp unquote(fun_name)({entry_key, entry_value}, unquote(vars.bytes)) do
95✔
583
            {map_entry, unquote(vars.rest)} =
95✔
584
              case Protox.Decode.parse_key(unquote(vars.bytes)) do
95✔
585
                # key
586
                {1, _, unquote(vars.rest)} ->
95✔
587
                  {res, unquote(vars.rest)} = unquote(key_parser)
95✔
588
                  {{res, entry_value}, unquote(vars.rest)}
95✔
589

590
                # value
591
                {2, _, unquote(vars.rest)} ->
95✔
592
                  {res, unquote(vars.rest)} = unquote(value_parser)
95✔
593
                  {{entry_key, res}, unquote(vars.rest)}
95✔
594

595
                {tag, wire_type, unquote(vars.rest)} ->
95✔
596
                  {_, unquote(vars.rest)} =
95✔
597
                    Protox.Decode.parse_unknown(tag, wire_type, unquote(vars.rest))
95✔
598

599
                  {{entry_key, entry_value}, unquote(vars.rest)}
95✔
600
              end
601

602
            unquote(fun_name)(map_entry, unquote(vars.rest))
95✔
603
          end
604
        end
605

606
      {fun_name, code}
607
    end)
608
    |> Enum.sort(fn {lhs_fun_name, _}, {rhs_fun_name, _} -> lhs_fun_name < rhs_fun_name end)
355✔
609
    |> Enum.dedup_by(fn {fun_name, _} -> fun_name end)
95✔
610
    |> Enum.map(fn {_, code} -> code end)
80✔
611
  end
612

613
  defp make_map_decode_fun_name(key_type, value_type) do
614
    value_name =
190✔
615
      case value_type do
616
        {:message, sub_msg} -> "msg_#{Atom.to_string(sub_msg)}"
20✔
617
        {:enum, enum} -> "enum_#{Atom.to_string(enum)}"
20✔
618
        ty -> "#{Atom.to_string(ty)}"
150✔
619
      end
620

621
    value_name =
190✔
622
      value_name
623
      |> Macro.underscore()
624
      |> String.replace("/", "_")
625

626
    String.to_atom("parse_#{Atom.to_string(key_type)}_#{value_name}")
190✔
627
  end
628

629
  defp make_parse_map_entry(vars, type) do
630
    parse_delimited =
190✔
631
      quote do
632
        {len, new_rest} = Protox.Varint.decode(unquote(vars.rest))
190✔
633
        {unquote(vars.delimited), new_rest} = Protox.Decode.parse_delimited(new_rest, len)
190✔
634

635
        {unquote(make_parse_delimited(vars.delimited, type)), new_rest}
190✔
636
      end
637

638
    case type do
190✔
639
      :string -> parse_delimited
35✔
640
      :bytes -> parse_delimited
5✔
641
      {:message, _} -> parse_delimited
10✔
642
      _ -> make_parse_single(vars.rest, type)
140✔
643
    end
644
  end
645
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc