diff --git a/spec/cbor/from_cbor_spec.cr b/spec/cbor/from_cbor_spec.cr new file mode 100644 index 0000000..73ddde4 --- /dev/null +++ b/spec/cbor/from_cbor_spec.cr @@ -0,0 +1,24 @@ +require "../spec_helper" + +describe "CBOR helpers on basic types" do + describe "#from_cbor" do + tests = [ + {String, Bytes[0x61, 0x61], "a"}, + {UInt8, Bytes[0x18, 0x18], 24}, + {UInt16, Bytes[0x19, 0x03, 0xe8], 1000}, + {UInt32, Bytes[0x1a, 0x00, 0x0f, 0x42, 0x40], 1000000}, + {UInt64, Bytes[0x1b, 0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x10, 0x00], 1000000000000}, + {Int8, Bytes[0x29], -10}, + {Bytes, Bytes[0x44, 0x01, 0x02, 0x03, 0x04], Bytes[0x01, 0x02, 0x03, 0x04]}, + ] + + tests.each do |tt| + type, bytes, want = tt + + it "decodes #{type.class}" do + res = type.from_cbor(bytes) + res.should eq(want) + end + end + end +end diff --git a/src/cbor/decoder.cr b/src/cbor/decoder.cr new file mode 100644 index 0000000..ffbc18a --- /dev/null +++ b/src/cbor/decoder.cr @@ -0,0 +1,50 @@ +class CBOR::Decoder + @lexer : Lexer + @current_token : Token::T? + + def initialize(input) + @lexer = Lexer.new(input) + @current_token = @lexer.next_token + end + + def read_string : String + case token = @current_token + when Token::StringT + finish_token! + token.value + when Token::BytesT + finish_token! + String.new(token.value) + else + unexpected_token(token, "StringT or BytesT") + end + end + + def read_int + read_type(Token::IntT) { |token| token.value } + end + + def read_bytes + read_type(Token::BytesT) { |token| token.value } + end + + private def finish_token! + @current_token = @lexer.next_token + end + + private macro read_type(type, finish_token = true, &block) + case token = @current_token + when {{type}} + {% if finish_token %}finish_token!{% end %} + {{ block.body }} + else + unexpected_token(token, {{type.stringify.split("::").last}}) + end + end + + private def unexpected_token(token, expected = nil) + message = "Unexpected token #{token.class}" + message += " expected #{expected}" if expected + raise ParseError.new(message) + end +end diff --git a/src/cbor/from_cbor.cr b/src/cbor/from_cbor.cr new file mode 100644 index 0000000..cf6e905 --- /dev/null +++ b/src/cbor/from_cbor.cr @@ -0,0 +1,24 @@ +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 %} + +def Slice.new(decoder : CBOR::Decoder) + decoder.read_bytes.to_slice +end diff --git a/src/cbor/lexer.cr b/src/cbor/lexer.cr index 67da52d..0cb7b05 100644 --- a/src/cbor/lexer.cr +++ b/src/cbor/lexer.cr @@ -1,10 +1,6 @@ class CBOR::Lexer BREAK = 0xff - def self.new(string : String) - new IO::Memory.new(string) - end - def self.new(slice : Bytes) new IO::Memory.new(slice) end