• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ahamez / protox / cee277094466d88a1d93f15382bdaf370f4f2b3d

19 Jan 2025 06:30AM UTC coverage: 94.088% (+1.0%) from 93.04%
cee277094466d88a1d93f15382bdaf370f4f2b3d

push

github

ahamez
doc: comment functionality availability

748 of 795 relevant lines covered (94.09%)

18699.39 hits per line

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

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

5
  alias Protox.Field
6

7
  use Protox.{
8
    Float,
9
    WireTypes
10
  }
11

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

23
    # The public function to decode the binary protobuf.
24
    decode_fun = make_decode_fun(required_fields, msg_name, vars)
90✔
25

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

30
    # The functions that decodes maps.
31
    parse_map_entries = make_parse_map_entries_funs(vars, fields)
90✔
32

33
    quote do
34
      unquote(decode_fun)
35
      unquote(parse_key_value_fun)
36
      unquote(parse_map_entries)
37
    end
38
  end
39

40
  defp make_decode_fun(required_fields, msg_name, vars) do
41
    decode_bang_fun = make_decode_bang_fun(required_fields, msg_name, vars)
90✔
42

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

54
      unquote(decode_bang_fun)
55
    end
56
  end
57

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

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

73
        case unquote(required_fields) -- unquote(vars.set_fields) do
5✔
74
          [] -> msg
75
          missing_fields -> raise Protox.RequiredFieldsError.new(missing_fields)
76
        end
77
      end
78
    end
79
  end
80

81
  defp make_parse_key_value_fun(required_fields, fields, vars, opts) do
82
    keep_set_fields = required_fields != []
90✔
83

84
    parse_key_value_body =
90✔
85
      make_parse_key_value_body(keep_set_fields, fields, vars, opts)
86

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

94
        defp parse_key_value(unquote(vars.set_fields), bytes, msg) do
5✔
95
          unquote(parse_key_value_body)
96
        end
97
      end
98
    else
99
      quote do
100
        @spec parse_key_value(binary(), struct()) :: struct()
101
        defp parse_key_value(<<>>, msg), do: msg
102

103
        defp parse_key_value(bytes, msg), do: unquote(parse_key_value_body)
104
      end
105
    end
106
  end
107

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

112
    # Fragment to parse unknown fields. Those are identified with an unknown tag.
113
    unknown_fields_name = Keyword.fetch!(opts, :unknown_fields_name)
90✔
114

115
    unknown_tag_case =
90✔
116
      make_parse_key_value_unknown(
117
        vars,
118
        keep_set_fields,
119
        unknown_fields_name
120
      )
121

122
    # Fragment to parse known fields.
123
    known_tags_case = make_parse_key_value_known(vars, fields, keep_set_fields)
90✔
124

125
    all_cases = tag_0_case ++ known_tags_case ++ unknown_tag_case
90✔
126

127
    if keep_set_fields do
90✔
128
      quote do
129
        {new_set_fields, unquote(vars.field), rest} =
5✔
130
          case Protox.Decode.parse_key(bytes) do
131
            unquote(all_cases)
132
          end
133

134
        msg_updated = struct(unquote(vars.msg), unquote(vars.field))
5✔
135
        parse_key_value(new_set_fields, rest, msg_updated)
136
      end
137
    else
138
      quote do
139
        {unquote(vars.field), rest} =
85✔
140
          case Protox.Decode.parse_key(bytes) do
141
            unquote(all_cases)
142
          end
143

144
        msg_updated = struct(unquote(vars.msg), unquote(vars.field))
85✔
145
        parse_key_value(rest, msg_updated)
146
      end
147
    end
148
  end
149

150
  defp make_parse_key_value_tag_0() do
90✔
151
    quote do
152
      {0, _, _} -> raise %Protox.IllegalTagError{}
153
    end
154
  end
155

156
  defp make_parse_key_value_known(vars, fields, keep_set_fields) do
157
    Enum.flat_map(fields, fn %Field{} = field ->
90✔
158
      single = make_single_case(vars, keep_set_fields, field)
975✔
159

160
      single_generated = single != []
975✔
161
      delimited = make_delimited_case(vars, keep_set_fields, single_generated, field)
975✔
162

163
      delimited ++ single
975✔
164
    end)
165
  end
166

167
  defp make_parse_key_value_unknown(vars, keep_set_fields, unknown_fields_name) do
168
    body =
90✔
169
      quote do
170
        {
171
          unquote(unknown_fields_name),
172
          # Order is important here, we want to keep the order of the unknown fields.
173
          unquote(vars.msg).unquote(unknown_fields_name) ++ [unquote(vars.value)]
90✔
174
        }
175
      end
176

177
    case_return =
90✔
178
      case keep_set_fields do
179
        true -> quote(do: {unquote(vars.set_fields), [unquote(body)], rest})
5✔
180
        # No need to maintain a list of set fields when the list of required fields is empty
181
        false -> quote(do: {[unquote(body)], rest})
85✔
182
      end
183

184
    quote do
185
      {tag, wire_type, rest} ->
186
        {unquote(vars.value), rest} = Protox.Decode.parse_unknown(tag, wire_type, rest)
90✔
187

188
        unquote(case_return)
189
    end
190
  end
191

192
  defp make_single_case(_vars, _keep_set_fields, %Field{type: {:message, _}}) do
225✔
193
    quote(do: [])
194
  end
195

196
  defp make_single_case(_vars, _keep_set_fields, %Field{type: :string}), do: quote(do: [])
105✔
197
  defp make_single_case(_vars, _keep_set_fields, %Field{type: :bytes}), do: quote(do: [])
20✔
198

199
  defp make_single_case(_vars, _keep_set_fields, %Field{type: {x, _}}) when x != :enum do
95✔
200
    quote(do: [])
201
  end
202

203
  defp make_single_case(vars, keep_set_fields, %Field{} = field) do
204
    parse_single = make_parse_single(vars.bytes, field.type)
530✔
205
    update_field = make_update_field(vars.value, field, vars, _wrap_value = true)
530✔
206

207
    # No need to maintain a list of set fields for proto3
208
    case_return =
530✔
209
      case keep_set_fields do
210
        true ->
211
          quote do: {[unquote(field.name) | set_fields], [unquote(update_field)], rest}
5✔
212

213
        false ->
525✔
214
          quote do: {[unquote(update_field)], rest}
215
      end
216

217
    quote do
218
      {unquote(field.tag), _, unquote(vars.bytes)} ->
530✔
219
        {value, rest} = unquote(parse_single)
220
        unquote(case_return)
221
    end
222
  end
223

224
  defp make_delimited_case(
225
         vars,
226
         keep_set_fields,
227
         single_generated,
228
         %Field{type: {:message, _}} = field
229
       ) do
230
    make_delimited_case_impl(vars, keep_set_fields, single_generated, field)
225✔
231
  end
232

233
  defp make_delimited_case(vars, keep_set_fields, single_generated, %Field{type: :bytes} = field) do
234
    make_delimited_case_impl(vars, keep_set_fields, single_generated, field)
20✔
235
  end
236

237
  defp make_delimited_case(vars, keep_set_fields, single_generated, %Field{type: :string} = field) do
238
    make_delimited_case_impl(vars, keep_set_fields, single_generated, field)
105✔
239
  end
240

241
  defp make_delimited_case(_vars, _keep_set_fields, _single_generated, %Field{kind: {:scalar, _}}) do
280✔
242
    []
243
  end
244

245
  defp make_delimited_case(_vars, _keep_set_fields, _single_generated, %Field{kind: {:oneof, _}}) do
35✔
246
    []
247
  end
248

249
  defp make_delimited_case(vars, keep_set_fields, single_generated, %Field{} = field) do
250
    make_delimited_case_impl(vars, keep_set_fields, single_generated, field)
310✔
251
  end
252

253
  defp make_delimited_case_impl(vars, keep_set_fields, single_generated, %Field{} = field) do
254
    # If the case to decode single occurrences of repeated elements has been generated,
255
    # it means that it's a repeated field of scalar elements (as non-scalar cannot be packed,
256
    # see https://developers.google.com/protocol-buffers/docs/encoding#optional).
257
    # Thus, it's useless to wrap in a list the result of the decoding as it means
258
    # we're using a parse_repeated_* function that always returns a list.
259
    update_field =
660✔
260
      if field.type == :bytes do
660✔
261
        make_update_field(vars.delimited, field, vars, _wrap_value = !single_generated)
20✔
262
      else
263
        parse_delimited = make_parse_delimited(vars.delimited, field.type)
640✔
264
        make_update_field(parse_delimited, field, vars, _wrap_value = !single_generated)
640✔
265
      end
266

267
    case_return =
660✔
268
      case keep_set_fields do
269
        true -> quote do: {[unquote(field.name) | set_fields], [unquote(update_field)], rest}
5✔
270
        false -> quote do: {[unquote(update_field)], rest}
655✔
271
      end
272

273
    # If `single` was not generated, then we don't need the `@wire_delimited discrimant
274
    # as there is only one clause for this `tag`.
275
    wire_type =
660✔
276
      case single_generated do
277
        true -> quote do: unquote(@wire_delimited)
215✔
278
        false -> quote do: _
279
      end
280

281
    quote do
282
      {unquote(field.tag), unquote(wire_type), unquote(vars.bytes)} ->
660✔
283
        {len, unquote(vars.bytes)} = Protox.Varint.decode(unquote(vars.bytes))
660✔
284
        {unquote(vars.delimited), rest} = Protox.Decode.parse_delimited(unquote(vars.bytes), len)
660✔
285
        unquote(case_return)
286
    end
287
  end
288

289
  defp make_update_field(value, %Field{kind: :map} = field, vars, _wrap_value) do
290
    quote do
291
      {entry_key, entry_value} = unquote(value)
292

293
      {unquote(field.name),
95✔
294
       Map.put(unquote(vars.msg).unquote(field.name), entry_key, entry_value)}
95✔
295
    end
296
  end
297

298
  defp make_update_field(
299
         value,
300
         %Field{label: :proto3_optional, kind: {:oneof, _}, type: {:message, _}} = field,
301
         vars,
302
         _wrap_value
303
       ) do
304
    quote do
305
      case unquote(vars.msg).unquote(field.name) do
×
306
        {unquote(field.name), previous_value} ->
×
307
          {unquote(field.name), Protox.MergeMessage.merge(previous_value, unquote(value))}
×
308

309
        _ ->
310
          {unquote(field.name), unquote(value)}
×
311
      end
312
    end
313
  end
314

315
  defp make_update_field(
316
         value,
317
         %Field{kind: {:oneof, parent_field}, type: {:message, _}} = field,
318
         vars,
319
         _wrap_value
320
       ) do
321
    quote do
322
      case unquote(vars.msg).unquote(parent_field) do
5✔
323
        {unquote(field.name), previous_value} ->
5✔
324
          {unquote(parent_field),
325
           {unquote(field.name), Protox.MergeMessage.merge(previous_value, unquote(value))}}
5✔
326

327
        _ ->
328
          {unquote(parent_field), {unquote(field.name), unquote(value)}}
5✔
329
      end
330
    end
331
  end
332

333
  defp make_update_field(value, %Field{kind: {:oneof, parent_field}} = field, _vars, _wrap_value) do
334
    case field.label do
45✔
335
      :proto3_optional ->
×
336
        quote(do: {unquote(field.name), unquote(value)})
×
337

338
      _ ->
45✔
339
        quote(do: {unquote(parent_field), {unquote(field.name), unquote(value)}})
45✔
340
    end
341
  end
342

343
  defp make_update_field(
120✔
344
         value,
345
         %Field{kind: {:scalar, _}, type: {:message, _}} = field,
346
         vars,
347
         _wrap_value
348
       ) do
349
    quote do
350
      {
351
        unquote(field.name),
120✔
352
        Protox.MergeMessage.merge(unquote(vars.msg).unquote(field.name), unquote(value))
120✔
353
      }
354
    end
355
  end
356

357
  defp make_update_field(value, %Field{kind: {:scalar, _}} = field, _vars, _wrap_value) do
375✔
358
    quote(do: {unquote(field.name), unquote(value)})
375✔
359
  end
360

361
  defp make_update_field(value, %Field{} = field, vars, true = _wrap_value) do
335✔
362
    quote do
363
      {unquote(field.name), unquote(vars.msg).unquote(field.name) ++ [unquote(value)]}
335✔
364
    end
365
  end
366

367
  defp make_update_field(value, %Field{} = field, vars, false = _wrap_value) do
215✔
368
    quote do
369
      {unquote(field.name), unquote(vars.msg).unquote(field.name) ++ unquote(value)}
215✔
370
    end
371
  end
372

373
  defp make_parse_delimited(bytes_var, :bytes) do
374
    quote(do: unquote(bytes_var))
5✔
375
  end
376

377
  defp make_parse_delimited(bytes_var, :string) do
378
    quote(do: Protox.Decode.validate_string!(unquote(bytes_var)))
379
  end
380

381
  defp make_parse_delimited(bytes_var, {:enum, mod}) do
382
    quote(do: Protox.Decode.parse_repeated_enum([], unquote(bytes_var), unquote(mod)))
383
  end
384

385
  defp make_parse_delimited(bytes_var, {:message, mod}) do
386
    quote(do: unquote(mod).decode!(unquote(bytes_var)))
387
  end
388

389
  defp make_parse_delimited(bytes_var, :bool) do
390
    quote(do: Protox.Decode.parse_repeated_bool([], unquote(bytes_var)))
391
  end
392

393
  defp make_parse_delimited(bytes_var, :int32) do
394
    quote(do: Protox.Decode.parse_repeated_int32([], unquote(bytes_var)))
395
  end
396

397
  defp make_parse_delimited(bytes_var, :uint32) do
398
    quote(do: Protox.Decode.parse_repeated_uint32([], unquote(bytes_var)))
399
  end
400

401
  defp make_parse_delimited(bytes_var, :sint32) do
402
    quote(do: Protox.Decode.parse_repeated_sint32([], unquote(bytes_var)))
403
  end
404

405
  defp make_parse_delimited(bytes_var, :int64) do
406
    quote(do: Protox.Decode.parse_repeated_int64([], unquote(bytes_var)))
407
  end
408

409
  defp make_parse_delimited(bytes_var, :uint64) do
410
    quote(do: Protox.Decode.parse_repeated_uint64([], unquote(bytes_var)))
411
  end
412

413
  defp make_parse_delimited(bytes_var, :sint64) do
414
    quote(do: Protox.Decode.parse_repeated_sint64([], unquote(bytes_var)))
415
  end
416

417
  defp make_parse_delimited(bytes_var, :fixed32) do
418
    quote(do: Protox.Decode.parse_repeated_fixed32([], unquote(bytes_var)))
419
  end
420

421
  defp make_parse_delimited(bytes_var, :fixed64) do
422
    quote(do: Protox.Decode.parse_repeated_fixed64([], unquote(bytes_var)))
423
  end
424

425
  defp make_parse_delimited(bytes_var, :sfixed32) do
426
    quote(do: Protox.Decode.parse_repeated_sfixed32([], unquote(bytes_var)))
427
  end
428

429
  defp make_parse_delimited(bytes_var, :sfixed64) do
430
    quote(do: Protox.Decode.parse_repeated_sfixed64([], unquote(bytes_var)))
431
  end
432

433
  defp make_parse_delimited(bytes_var, :float) do
434
    quote(do: Protox.Decode.parse_repeated_float([], unquote(bytes_var)))
435
  end
436

437
  defp make_parse_delimited(bytes_var, :double) do
438
    quote(do: Protox.Decode.parse_repeated_double([], unquote(bytes_var)))
439
  end
440

441
  defp make_parse_delimited(bytes_var, {key_type, value_type}) do
442
    unset_map_value =
95✔
443
      case value_type do
444
        {:message, msg_type} -> quote(do: struct(unquote(msg_type)))
445
        _ -> quote(do: Protox.Default.default(unquote(value_type)))
446
      end
447

448
    parser_fun_name = make_map_decode_fun_name(key_type, value_type)
95✔
449

450
    quote do
451
      {map_key, map_value} = unquote(parser_fun_name)({:unset, :unset}, unquote(bytes_var))
452

453
      map_key =
454
        case map_key do
455
          :unset -> Protox.Default.default(unquote(key_type))
456
          _ -> map_key
457
        end
458

459
      map_value =
460
        case map_value do
461
          :unset -> unquote(unset_map_value)
462
          _ -> map_value
463
        end
464

465
      {map_key, map_value}
466
    end
467
  end
468

469
  defp make_parse_single(bytes_var, :double) do
470
    quote(do: Protox.Decode.parse_double(unquote(bytes_var)))
471
  end
472

473
  defp make_parse_single(bytes_var, :float) do
474
    quote(do: Protox.Decode.parse_float(unquote(bytes_var)))
475
  end
476

477
  defp make_parse_single(bytes_var, :sfixed64) do
478
    quote(do: Protox.Decode.parse_sfixed64(unquote(bytes_var)))
479
  end
480

481
  defp make_parse_single(bytes_var, :fixed64) do
482
    quote(do: Protox.Decode.parse_fixed64(unquote(bytes_var)))
483
  end
484

485
  defp make_parse_single(bytes_var, :sfixed32) do
486
    quote(do: Protox.Decode.parse_sfixed32(unquote(bytes_var)))
487
  end
488

489
  defp make_parse_single(bytes_var, :fixed32) do
490
    quote(do: Protox.Decode.parse_fixed32(unquote(bytes_var)))
491
  end
492

493
  defp make_parse_single(bytes_var, :bool) do
494
    quote(do: Protox.Decode.parse_bool(unquote(bytes_var)))
495
  end
496

497
  defp make_parse_single(bytes_var, :sint32) do
498
    quote(do: Protox.Decode.parse_sint32(unquote(bytes_var)))
499
  end
500

501
  defp make_parse_single(bytes_var, :sint64) do
502
    quote(do: Protox.Decode.parse_sint64(unquote(bytes_var)))
503
  end
504

505
  defp make_parse_single(bytes_var, :uint32) do
506
    quote(do: Protox.Decode.parse_uint32(unquote(bytes_var)))
507
  end
508

509
  defp make_parse_single(bytes_var, :uint64) do
510
    quote(do: Protox.Decode.parse_uint64(unquote(bytes_var)))
511
  end
512

513
  defp make_parse_single(bytes_var, :int32) do
514
    quote(do: Protox.Decode.parse_int32(unquote(bytes_var)))
515
  end
516

517
  defp make_parse_single(bytes_var, :int64) do
518
    quote(do: Protox.Decode.parse_int64(unquote(bytes_var)))
519
  end
520

521
  defp make_parse_single(bytes_var, {:enum, mod}) do
522
    quote(do: Protox.Decode.parse_enum(unquote(bytes_var), unquote(mod)))
523
  end
524

525
  defp make_parse_map_entries_funs(vars, fields) do
526
    {maps, _other_fields} = Protox.Defs.split_maps(fields)
90✔
527

528
    maps
529
    |> Enum.map(fn %Field{kind: :map} = field ->
530
      key_type = elem(field.type, 0)
95✔
531
      value_type = elem(field.type, 1)
95✔
532

533
      fun_name = make_map_decode_fun_name(key_type, value_type)
95✔
534

535
      key_parser = make_parse_map_entry(vars, key_type)
95✔
536
      value_parser = make_parse_map_entry(vars, value_type)
95✔
537

538
      code =
95✔
539
        quote do
540
          defp unquote(fun_name)(map_entry, <<>>) do
541
            map_entry
542
          end
543

544
          # https://developers.google.com/protocol-buffers/docs/proto3#backwards-compatibility
545
          # Maps are equivalent to:
546
          #   message MapFieldEntry {
547
          #     key_type key = 1;
548
          #     value_type value = 2;
549
          #   }
550
          # repeated MapFieldEntry map_field = N;
551
          #
552
          defp unquote(fun_name)({entry_key, entry_value}, unquote(vars.bytes)) do
95✔
553
            {map_entry, unquote(vars.rest)} =
95✔
554
              case Protox.Decode.parse_key(unquote(vars.bytes)) do
95✔
555
                # key
556
                {1, _, unquote(vars.rest)} ->
95✔
557
                  {res, unquote(vars.rest)} = unquote(key_parser)
95✔
558
                  {{res, entry_value}, unquote(vars.rest)}
95✔
559

560
                # value
561
                {2, _, unquote(vars.rest)} ->
95✔
562
                  {res, unquote(vars.rest)} = unquote(value_parser)
95✔
563
                  {{entry_key, res}, unquote(vars.rest)}
95✔
564

565
                {tag, wire_type, unquote(vars.rest)} ->
95✔
566
                  {_, unquote(vars.rest)} =
95✔
567
                    Protox.Decode.parse_unknown(tag, wire_type, unquote(vars.rest))
95✔
568

569
                  {{entry_key, entry_value}, unquote(vars.rest)}
95✔
570
              end
571

572
            unquote(fun_name)(map_entry, unquote(vars.rest))
95✔
573
          end
574
        end
575

576
      {fun_name, code}
577
    end)
578
    |> Enum.sort(fn {lhs_fun_name, _}, {rhs_fun_name, _} -> lhs_fun_name < rhs_fun_name end)
355✔
579
    |> Enum.dedup_by(fn {fun_name, _} -> fun_name end)
95✔
580
    |> Enum.map(fn {_, code} -> code end)
90✔
581
  end
582

583
  defp make_map_decode_fun_name(key_type, value_type) do
584
    value_name =
190✔
585
      case value_type do
586
        {:message, sub_msg} -> "msg_#{Atom.to_string(sub_msg)}"
20✔
587
        {:enum, enum} -> "enum_#{Atom.to_string(enum)}"
20✔
588
        ty -> "#{Atom.to_string(ty)}"
150✔
589
      end
590

591
    value_name =
190✔
592
      value_name
593
      |> Macro.underscore()
594
      |> String.replace("/", "_")
595

596
    String.to_atom("parse_#{Atom.to_string(key_type)}_#{value_name}")
190✔
597
  end
598

599
  defp make_parse_map_entry(vars, type) do
600
    parse_delimited =
190✔
601
      quote do
602
        {len, new_rest} = Protox.Varint.decode(unquote(vars.rest))
190✔
603
        {unquote(vars.delimited), new_rest} = Protox.Decode.parse_delimited(new_rest, len)
190✔
604

605
        {unquote(make_parse_delimited(vars.delimited, type)), new_rest}
190✔
606
      end
607

608
    case type do
190✔
609
      :string -> parse_delimited
35✔
610
      :bytes -> parse_delimited
5✔
611
      {:message, _} -> parse_delimited
10✔
612
      _ -> make_parse_single(vars.rest, type)
140✔
613
    end
614
  end
615
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