~arestifo/crystal-cbor

crystal-cbor: Decoding: use_cbor_discriminator v1 REJECTED

~karchnu: 1
 Decoding: use_cbor_discriminator

 4 files changed, 43 insertions(+), 0 deletions(-)
#339292 .build.yml success
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~arestifo/crystal-cbor/patches/14898/mbox | git am -3
Learn more about email & git

[PATCH crystal-cbor] Decoding: use_cbor_discriminator Export this patch

From: Karchnu <karchnu@karchnu.fr>

---
I added the `use_cbor_discriminator`.

This is a slow version, I read
the whole buffer twice. Still, I think this can be useful.

It can be
easily improved:
- if we can assume that the discriminator is the first
attribute in the buffer,
  we could just read it and reset the decoder
- if we read and decode the whole content (which is currently the case),
we should be able to cast the result into the final object.
  I don't
know how to do that in Crystal, but it sounds like the right thing to
do.

 src/cbor/decoder.cr      |  5 +++++
 src/cbor/from_cbor.cr    |  4 ++++
 src/cbor/lexer.cr        |  5 +++++
 src/cbor/serializable.cr | 29 +++++++++++++++++++++++++++++
 4 files changed, 43 insertions(+)

diff --git a/src/cbor/decoder.cr b/src/cbor/decoder.cr
index 66ba0f7..ae567df 100644
--- a/src/cbor/decoder.cr
+++ b/src/cbor/decoder.cr
@@ -2,6 +2,11 @@ class CBOR::Decoder
  @lexer : Lexer
  getter current_token : Token::T?

  def reset
    @lexer.reset
    @current_token = @lexer.next_token
  end

  def initialize(input)
    @lexer = Lexer.new(input)
    @current_token = @lexer.next_token
diff --git a/src/cbor/from_cbor.cr b/src/cbor/from_cbor.cr
index e2610e7..0374c1f 100644
--- a/src/cbor/from_cbor.cr
+++ b/src/cbor/from_cbor.cr
@@ -3,6 +3,10 @@ def Object.from_cbor(string_or_io)
  new(parser)
end

def Object.from_cbor(parser : CBOR::Decoder)
  new(parser)
end

def String.new(decoder : CBOR::Decoder)
  decoder.read_string
end
diff --git a/src/cbor/lexer.cr b/src/cbor/lexer.cr
index 8ba2a11..cc34491 100644
--- a/src/cbor/lexer.cr
+++ b/src/cbor/lexer.cr
@@ -8,6 +8,11 @@ class CBOR::Lexer
  def initialize(@io : IO)
  end

  def reset
    @io.seek 0
    @eof = false
  end

  def next_token : Token::T?
    return nil if @eof

diff --git a/src/cbor/serializable.cr b/src/cbor/serializable.cr
index b1fe894..b55258a 100644
--- a/src/cbor/serializable.cr
+++ b/src/cbor/serializable.cr
@@ -346,6 +346,35 @@ module CBOR
      end
    end

    macro use_cbor_discriminator(field, mapping)
      {% unless mapping.is_a?(HashLiteral) || mapping.is_a?(NamedTupleLiteral) %}
        {% mapping.raise "mapping argument must be a HashLiteral or a NamedTupleLiteral, not #{mapping.class_name.id}" %}
      {% end %}

      # SLOW. Read everything, get the type, read everything again.
      def self.new(decoder : ::CBOR::Decoder)
        if v = decoder.read_value
          decoder.reset
          case v
          when Hash(CBOR::Type, CBOR::Type)
            discriminator_value = v[{{field.id.stringify}}]?
            case discriminator_value
            {% for key, value in mapping %}
              when {{key.id.stringify}}
                return {{value.id}}.from_cbor(decoder)
            {% end %}
            else
              raise "Unknown '{{field.id}}' discriminator value: #{discriminator_value.inspect}"
            end
          else
            raise "cannot get cbor discriminator #{ {{ field.id.stringify }} }"
          end
        else
          raise "cannot decode cbor value"
        end
      end
    end

    # Tells this class to decode CBOR by using a field as a discriminator.
    #
    # - *field* must be the field name to use as a discriminator
-- 
2.26.2
crystal-cbor/patches/.build.yml: SUCCESS in 26s

[Decoding: use_cbor_discriminator][0] from [~karchnu][1]

[0]: https://lists.sr.ht/~arestifo/crystal-cbor/patches/14898
[1]: mailto:karchnu@karchnu.fr

✓ #339292 SUCCESS crystal-cbor/patches/.build.yml https://builds.sr.ht/~arestifo/job/339292