2020-04-23 00:25:11 +02:00
|
|
|
def Object.from_cbor(string_or_io)
|
|
|
|
parser = CBOR::Decoder.new(string_or_io)
|
|
|
|
new(parser)
|
|
|
|
end
|
|
|
|
|
|
|
|
def String.new(decoder : CBOR::Decoder)
|
|
|
|
decoder.read_string
|
|
|
|
end
|
|
|
|
|
|
|
|
{% for size in [8, 16, 32, 64] %}
|
|
|
|
|
|
|
|
def Int{{size.id}}.new(decoder : CBOR::Decoder)
|
|
|
|
decoder.read_int.to_i{{size.id}}
|
|
|
|
end
|
|
|
|
|
|
|
|
def UInt{{size.id}}.new(decoder : CBOR::Decoder)
|
|
|
|
decoder.read_int.to_u{{size.id}}
|
|
|
|
end
|
|
|
|
|
|
|
|
{% end %}
|
|
|
|
|
2020-04-25 18:35:07 +02:00
|
|
|
def Int128.new(decoder : CBOR::Decoder)
|
|
|
|
decoder.read_int.to_i128
|
|
|
|
end
|
|
|
|
|
2020-04-24 22:57:24 +02:00
|
|
|
{% for size in [32, 64] %}
|
|
|
|
|
|
|
|
def Float{{size.id}}.new(decoder : CBOR::Decoder)
|
|
|
|
decoder.read_float.to_f{{size.id}}
|
|
|
|
end
|
|
|
|
|
|
|
|
{% end %}
|
|
|
|
|
2020-04-24 15:59:16 +02:00
|
|
|
def Bool.new(decoder : CBOR::Decoder)
|
|
|
|
decoder.read_bool
|
|
|
|
end
|
|
|
|
|
2020-04-24 22:50:48 +02:00
|
|
|
def Nil.new(decoder : CBOR::Decoder)
|
|
|
|
decoder.read_nil
|
|
|
|
end
|
|
|
|
|
2020-04-23 00:25:11 +02:00
|
|
|
def Slice.new(decoder : CBOR::Decoder)
|
|
|
|
decoder.read_bytes.to_slice
|
|
|
|
end
|
2020-04-24 15:59:16 +02:00
|
|
|
|
2020-04-24 23:43:57 +02:00
|
|
|
def Array.new(decoder : CBOR::Decoder)
|
|
|
|
arr = new
|
|
|
|
decoder.consume_array { arr << T.new(decoder) }
|
|
|
|
arr
|
|
|
|
end
|
|
|
|
|
2020-04-25 17:27:01 +02:00
|
|
|
def Set.new(decoder : CBOR::Decoder)
|
|
|
|
set = new
|
|
|
|
decoder.consume_array { set << T.new(decoder) }
|
|
|
|
set
|
|
|
|
end
|
|
|
|
|
|
|
|
def Hash.new(decoder : CBOR::Decoder)
|
|
|
|
hash = new
|
|
|
|
decoder.consume_hash do
|
|
|
|
k = K.new(decoder)
|
|
|
|
hash[k] = V.new(decoder)
|
|
|
|
end
|
|
|
|
hash
|
|
|
|
end
|
|
|
|
|
2020-04-25 17:33:19 +02:00
|
|
|
def Enum.new(decoder : CBOR::Decoder)
|
|
|
|
case token = decoder.current_token
|
|
|
|
when CBOR::Token::IntT
|
|
|
|
decoder.finish_token!
|
|
|
|
from_value(token.value)
|
|
|
|
when CBOR::Token::StringT
|
|
|
|
decoder.finish_token!
|
|
|
|
parse(token.value)
|
|
|
|
else
|
|
|
|
decoder.unexpected_token(token, "IntT or StringT")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-25 17:44:14 +02:00
|
|
|
def Tuple.new(decoder : CBOR::Decoder)
|
|
|
|
{% begin %}
|
|
|
|
token = decoder.current_token
|
|
|
|
unless token.is_a?(CBOR::Token::ArrayT)
|
|
|
|
raise decoder.unexpected_token(token, "ArrayT")
|
|
|
|
end
|
|
|
|
|
|
|
|
size = token.size
|
|
|
|
|
|
|
|
raise CBOR::ParseError.new("Cannot read indefinite size array as Tuple") unless size
|
|
|
|
|
|
|
|
unless {{ @type.size }} <= size
|
|
|
|
raise CBOR::ParseError.new("Expected array with size #{ {{ @type.size }} }, but got #{size}")
|
|
|
|
end
|
|
|
|
decoder.finish_token!
|
|
|
|
|
|
|
|
value = Tuple.new(
|
|
|
|
{% for i in 0...@type.size %}
|
|
|
|
(self[{{i}}].new(decoder)),
|
|
|
|
{% end %}
|
|
|
|
)
|
|
|
|
|
|
|
|
value
|
|
|
|
{% end %}
|
|
|
|
end
|
|
|
|
|
2020-04-25 18:13:56 +02:00
|
|
|
def NamedTuple.new(decoder : CBOR::Decoder)
|
|
|
|
{% begin %}
|
|
|
|
{% for key in T.keys %}
|
|
|
|
%var{key.id} = nil
|
|
|
|
{% end %}
|
|
|
|
|
|
|
|
decoder.consume_hash do
|
|
|
|
key = decoder.read_string
|
|
|
|
case key
|
|
|
|
{% for key, type in T %}
|
|
|
|
when {{key.stringify}}
|
|
|
|
%var{key.id} = {{type}}.new(decoder)
|
|
|
|
{% end %}
|
|
|
|
else
|
|
|
|
raise CBOR::ParseError.new("Missing attribute: #{key}")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
{
|
|
|
|
{% for key, type in T %}
|
|
|
|
{{key}}: %var{key.id}.as({{type}}),
|
|
|
|
{% end %}
|
|
|
|
}
|
|
|
|
{% end %}
|
|
|
|
end
|
|
|
|
|
2020-04-24 22:50:48 +02:00
|
|
|
# Reads the CBOR values as a time. The value must be surrounded by a time tag as
|
2020-04-24 15:59:16 +02:00
|
|
|
# specified by [Section 2.4.1 of RFC 7049][1].
|
|
|
|
#
|
|
|
|
# [1]: https://tools.ietf.org/html/rfc7049#section-2.4.1
|
|
|
|
def Time.new(decoder : CBOR::Decoder)
|
|
|
|
case tag = decoder.read_tag
|
|
|
|
when CBOR::Tag::RFC3339Time
|
|
|
|
Time::Format::RFC_3339.parse(decoder.read_string)
|
|
|
|
when CBOR::Tag::EpochTime
|
|
|
|
case num = decoder.read_num
|
|
|
|
when Int
|
|
|
|
Time.unix(num)
|
|
|
|
when Float
|
2020-04-24 22:46:07 +02:00
|
|
|
Time.unix_ms((BigFloat.new(num) * 1_000).to_u64)
|
2020-04-24 15:59:16 +02:00
|
|
|
end
|
|
|
|
else
|
2020-04-25 18:35:07 +02:00
|
|
|
raise CBOR::ParseError.new("Expected tag to have value 0 or 1, got #{tag.value}")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Reads the CBOR value as a BigInt. The value must be surrounded by a tag with
|
|
|
|
# value 2 (positive) or 3 (negative).
|
|
|
|
def BigInt.new(decoder : CBOR::Decoder)
|
|
|
|
case tag = decoder.read_tag
|
|
|
|
when CBOR::Tag::PositiveBigNum,
|
|
|
|
CBOR::Tag::NegativeBigNum
|
|
|
|
big = new(decoder.read_bytes.hexstring, 16)
|
|
|
|
|
|
|
|
if tag == CBOR::Tag::NegativeBigNum
|
|
|
|
big *= -1
|
|
|
|
big -= 1
|
|
|
|
end
|
|
|
|
|
|
|
|
big
|
|
|
|
else
|
|
|
|
raise CBOR::ParseError.new("Expected tag to have value 2 or 3, got #{tag.value}")
|
2020-04-24 15:59:16 +02:00
|
|
|
end
|
|
|
|
end
|
2020-04-24 23:43:57 +02:00
|
|
|
|
|
|
|
def Union.new(decoder : CBOR::Decoder)
|
|
|
|
{% begin %}
|
2020-04-25 17:27:01 +02:00
|
|
|
case decoder.current_token
|
|
|
|
{% if T.includes? Nil %}
|
|
|
|
when CBOR::Token::SimpleValueT
|
|
|
|
return decoder.read_nil
|
|
|
|
{% end %}
|
|
|
|
{% if T.includes? Bool %}
|
|
|
|
when CBOR::Token::BoolT
|
|
|
|
return decoder.read_bool
|
|
|
|
{% end %}
|
|
|
|
{% if T.includes? String %}
|
|
|
|
when CBOR::Token::StringT
|
|
|
|
return decoder.read_string
|
2020-04-24 23:43:57 +02:00
|
|
|
{% end %}
|
2020-04-25 17:27:01 +02:00
|
|
|
when CBOR::Token::IntT
|
|
|
|
{% type_order = [Int64, UInt64, Int32, UInt32, Int16, UInt16, Int8, UInt8, Float64, Float32] %}
|
|
|
|
{% for type in type_order.select { |t| T.includes? t } %}
|
|
|
|
return {{type}}.new(decoder)
|
|
|
|
{% end %}
|
|
|
|
when CBOR::Token::FloatT
|
|
|
|
{% type_order = [Float64, Float32] %}
|
|
|
|
{% for type in type_order.select { |t| T.includes? t } %}
|
|
|
|
return {{type}}.new(decoder)
|
|
|
|
{% end %}
|
|
|
|
end
|
|
|
|
{% end %}
|
|
|
|
|
|
|
|
{% begin %}
|
|
|
|
{% primitive_types = [Nil, Bool, String] + Number::Primitive.union_types %}
|
|
|
|
{% non_primitives = T.reject { |t| primitive_types.includes? t } %}
|
2020-04-24 23:43:57 +02:00
|
|
|
|
|
|
|
# If after traversing all the types we are left with just one
|
|
|
|
# non-primitive type, we can parse it directly (no need to use `read_raw`)
|
|
|
|
{% if non_primitives.size == 1 %}
|
|
|
|
return {{non_primitives[0]}}.new(decoder)
|
|
|
|
{% else %}
|
2020-04-25 17:27:01 +02:00
|
|
|
{% for type in non_primitives %}
|
|
|
|
begin
|
2020-04-25 18:26:06 +02:00
|
|
|
return {{type}}.new(decoder)
|
2020-04-25 17:27:01 +02:00
|
|
|
rescue CBOR::ParseError
|
|
|
|
# Ignore
|
|
|
|
end
|
|
|
|
{% end %}
|
2020-04-25 18:26:06 +02:00
|
|
|
raise CBOR::ParseError.new("Couldn't parse #{self}")
|
2020-04-25 17:27:01 +02:00
|
|
|
{% end %}
|
2020-04-24 23:43:57 +02:00
|
|
|
{% end %}
|
|
|
|
end
|