Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gleam.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ repository = { type = "github", user = "Beaudidly", repo = "https://github.com/B
[dependencies]
gleam_stdlib = ">= 0.44.0 and < 2.0.0"
ieee_float = ">= 1.5.0 and < 2.0.0"
gleam_erlang = ">= 1.2.0 and < 2.0.0"

[dev-dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"
Expand Down
51 changes: 51 additions & 0 deletions src/gbor/decode.gleam
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import gleam/bit_array
import gleam/bool
import gleam/dynamic
import gleam/dynamic/decode as gdd
import gleam/erlang/atom
import gleam/int
import gleam/list
import gleam/result
import gleam/string
Expand All @@ -17,6 +20,46 @@ pub type CborDecodeError {
UnassignedError
}

pub fn cbor_to_dynamic(cbor: g.CBOR) -> dynamic.Dynamic {
case cbor {
g.CBInt(v) -> dynamic.int(v)
g.CBString(v) -> dynamic.string(v)
g.CBFloat(v) -> dynamic.float(v)
g.CBMap(v) ->
dynamic.properties(
list.map(v, fn(v) { #(cbor_to_dynamic(v.0), cbor_to_dynamic(v.1)) }),
)
g.CBArray(v) -> dynamic.array(list.map(v, cbor_to_dynamic))
g.CBBool(v) -> dynamic.bool(v)
g.CBNull -> dynamic.nil()
g.CBUndefined -> dynamic.nil()
g.CBBinary(v) -> dynamic.bit_array(v)
g.CBTagged(tag, value) -> ffi_to_tagged(tag, cbor_to_dynamic(value))
}
}

pub fn tagged_decoder(
expected_tag: Int,
decoder: gdd.Decoder(a),
zero: a,
) -> gdd.Decoder(a) {
let error = gdd.failure(zero, "CBOR tagged value")

use cbor_tag <- gdd.field(0, atom.decoder())
use <- bool.guard(
cbor_tag != atom.create("cbor_tagged__"),
gdd.failure(zero, "CBOR tagged value"),
)

use tag <- gdd.field(1, gdd.int)
use <- bool.guard(
tag != expected_tag,
gdd.failure(zero, int.to_string(expected_tag)),
)

gdd.at([2], decoder)
}

pub fn decode(data: BitArray) -> Result(#(g.CBOR, BitArray), CborDecodeError) {
case data {
<<0:3, min:5, rest:bits>> -> decode_uint(min, rest)
Expand Down Expand Up @@ -315,3 +358,11 @@ pub fn decode_tagged(

Ok(#(g.CBTagged(tag_num, tag_value), rest))
}

@external(erlang, "erl_gbor", "to_tagged")
pub fn ffi_to_tagged(tag: Int, value: dynamic.Dynamic) -> dynamic.Dynamic

@external(erlang, "erl_gbor", "check_tagged")
pub fn ffi_check_tagged(
tagged: dynamic.Dynamic,
) -> Result(#(Int, dynamic.Dynamic), String)
15 changes: 15 additions & 0 deletions src/gbor/erl_gbor.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-module(erl_gbor).

-export([to_tagged/2, check_tagged/1]).

to_tagged(Tag, Value) ->
{cbor_tagged__, Tag, Value}.


check_tagged(Tagged) ->
case Tagged of
{cbor_tagged__, Tag, Value} ->
{ok, {Tag, Value}};
_ ->
{error, invalid_tagged_value}
end.
30 changes: 30 additions & 0 deletions test/round_trip_test.gleam
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import gleam/bit_array
import gleam/bool
import gleam/dynamic/decode as gdd
import gleam/list
import gleam/result
import gleeunit

import gbor as g
Expand Down Expand Up @@ -99,3 +103,29 @@ pub fn decode_taggded_test() {

round_trip(g.CBTagged(1, g.CBInt(1_363_896_240)), "c11a514b67b0")
}

type Cat {
Cat(name: String, dob: String)
}

pub fn decode_dynamic_test() {
let assert Ok(bin) =
bit_array.base16_decode(
"A2646E616D6574323031332D30332D32315432303A30343A30305A63646F62C074323031332D30332D32315432303A30343A30305A",
)

let assert Ok(#(cbor_val, <<>>)) = d.decode(bin)
let dyn_val = d.cbor_to_dynamic(cbor_val)

let decoder = {
use name <- gdd.field("name", gdd.string)
use dob <- gdd.field("dob", d.tagged_decoder(0, gdd.string, "INVALID"))
gdd.success(Cat(name, dob))
}

let assert Ok(v) = gdd.run(dyn_val, decoder)
assert v == Cat("2013-03-21T20:04:00Z", "2013-03-21T20:04:00Z")

let assert Ok(encoded) = e.to_bit_array(cbor_val)
assert encoded == bin
}