diff --git a/spec/cbor/from_cbor_spec.cr b/spec/cbor/from_cbor_spec.cr index 73ddde4..fc818ca 100644 --- a/spec/cbor/from_cbor_spec.cr +++ b/spec/cbor/from_cbor_spec.cr @@ -9,7 +9,14 @@ describe "CBOR helpers on basic types" do {UInt32, Bytes[0x1a, 0x00, 0x0f, 0x42, 0x40], 1000000}, {UInt64, Bytes[0x1b, 0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x10, 0x00], 1000000000000}, {Int8, Bytes[0x29], -10}, + {Bool, Bytes[0xf4], false}, + {Bool, Bytes[0xf5], true}, {Bytes, Bytes[0x44, 0x01, 0x02, 0x03, 0x04], Bytes[0x01, 0x02, 0x03, 0x04]}, + {Time, + Bytes[0xc0, 0x74, 0x32, 0x30, 0x31, 0x33, 0x2d, 0x30, 0x33, 0x2d, 0x32, 0x31, 0x54, 0x32, 0x30, 0x3a, 0x30, 0x34, 0x3a, 0x30, 0x30, 0x5a], + Time::Format::RFC_3339.parse("2013-03-21T20:04:00Z")}, + {Time, Bytes[0xc1, 0x1a, 0x51, 0x4b, 0x67, 0xb0], Time.unix(1363896240)}, + {Time, Bytes[0xc1, 0xfb, 0x41, 0xd4, 0x52, 0xd9, 0xec, 0x20, 0x00, 0x00], Time.unix_ms((1363896240.5 * 1000).to_i)}, ] tests.each do |tt| diff --git a/src/cbor/decoder.cr b/src/cbor/decoder.cr index ffbc18a..7c97a13 100644 --- a/src/cbor/decoder.cr +++ b/src/cbor/decoder.cr @@ -24,15 +24,52 @@ class CBOR::Decoder read_type(Token::IntT) { |token| token.value } end + def read_float + read_type(Token::FloatT) { |token| token.value } + end + + def read_num + case token = @current_token + when Token::IntT, Token::FloatT + token.value + else + unexpected_token(token, "Token::IntT or Token::FloatT") + end + end + def read_bytes read_type(Token::BytesT) { |token| token.value } end + def read_tag : Tag + read_type(Token::TagT) { |token| token.value } + end + + def read_bool : Bool + read_type(Token::SimpleValueT) do |token| + case token.value + when SimpleValue::False + false + when SimpleValue::True + true + else + unexpected_token(token, "SimpleValue::True or SimpleValue::False") + end + end + end + private def finish_token! @current_token = @lexer.next_token end - private macro read_type(type, finish_token = true, &block) + private macro read_type(type, finish_token = true, ignore_tag = true, &block) + # Skip the tag unless the token we want to read is a tag + {% if type != Token::TagT && ignore_tag %} + if @current_token.is_a?(Token::TagT) + finish_token! + end + {% end %} + case token = @current_token when {{type}} {% if finish_token %}finish_token!{% end %} diff --git a/src/cbor/from_cbor.cr b/src/cbor/from_cbor.cr index cf6e905..7a88aee 100644 --- a/src/cbor/from_cbor.cr +++ b/src/cbor/from_cbor.cr @@ -19,6 +19,30 @@ end {% end %} +def Bool.new(decoder : CBOR::Decoder) + decoder.read_bool +end + def Slice.new(decoder : CBOR::Decoder) decoder.read_bytes.to_slice end + +# Reads the CBOR values a time. The value must be surrounded by a time tag as +# 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 + Time.unix_ms((num * 1000).to_i) + end + else + raise CBOR::ParseError.new("Expected tag to have value 0 or 1, got #{tag.value.to_s}") + end +end