From b8ebc57bb7a7a26955c9204cac8031f93a4e5403 Mon Sep 17 00:00:00 2001 From: Karchnu Date: Wed, 11 Nov 2020 04:54:19 +0100 Subject: [PATCH] Decoding: use_cbor_discriminator --- 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