diff --git a/lib/ssb_bfe.ex b/lib/ssb_bfe.ex index ff8ea35..b248df6 100644 --- a/lib/ssb_bfe.ex +++ b/lib/ssb_bfe.ex @@ -16,6 +16,8 @@ defmodule SsbBfe do :world end + # ENCODE + def encode(value) when is_list(value) do Enum.map(value, fn x -> encode(x) end) end @@ -31,7 +33,8 @@ defmodule SsbBfe do end def encode(value) when is_tuple(value) do - Enum.map(Tuple.to_list(value), fn x -> encode(x) end) + Enum.map(Tuple.to_list(value), fn x -> encode(x) end) |> + List.to_tuple() end def encode(value) when is_bitstring(value) do @@ -61,4 +64,54 @@ defmodule SsbBfe do def encode(value) when is_number(value), do: value def encode(value) when is_nil(value), do: SsbBfe.Encoder.encode_nil() + + # DECODE + + def decode(value) when is_binary(value) do + first_byte = :binary.first(value) + + cond do + 0 == first_byte -> + SsbBfe.Decoder.decode_feed(value) + + 1 == first_byte -> + SsbBfe.Decoder.decode_msg(value) + + 2 == first_byte -> + SsbBfe.Decoder.decode_blob(value) + + 4 == first_byte -> + SsbBfe.Decoder.decode_sig(value) + + 5 == first_byte -> + SsbBfe.Decoder.decode_box(value) + + 6 == first_byte -> + SsbBfe.Decoder.decode_generic(value) + + nil -> + true + end + end + + def decode(value) when is_number(value), do: value + + def decode(value) when is_list(value) do + Enum.map(value, fn x -> decode(x) end) + end + + def decode(value) when is_map(value) do + Enum.reduce( + value, + %{}, + fn {k, v}, acc -> + Map.put(acc, k, SsbBfe.decode(v)) + end + ) + end + + def decode(value) when is_tuple(value) do + Enum.map(Tuple.to_list(value), fn x -> decode(x) end) |> + List.to_tuple() + end end diff --git a/lib/ssb_bfe/decoder.ex b/lib/ssb_bfe/decoder.ex new file mode 100644 index 0000000..74fe5aa --- /dev/null +++ b/lib/ssb_bfe/decoder.ex @@ -0,0 +1,75 @@ +defmodule SsbBfe.Decoder do + defp extract_base64_data(bin, tf_tag) do + [_, base64_data] = :binary.split(bin, tf_tag) + Base.encode64(base64_data) + end + + def decode_blob(blob) do + encoded_base64_data = extract_base64_data(blob, <<2, 0>>) + "&" <> encoded_base64_data <> ".sha256" + end + + def decode_box(box) do + tf_tag = binary_part(box, 0, 2) + decode_box(box, tf_tag) + end + + # Matches box. + def decode_box(box, <<5, 0>>) do + encoded_base64_data = extract_base64_data(box, <<5, 0>>) + encoded_base64_data <> ".box" + end + + # Matches box2. + def decode_box(box, <<5, 1>>) do + encoded_base64_data = extract_base64_data(box, <<5, 1>>) + encoded_base64_data <> ".box2" + end + + def decode_feed(feed) do + tf_tag = binary_part(feed, 0, 2) + decode_feed(feed, tf_tag) + end + + # Matches classic feed. + def decode_feed(feed, <<0, 0>>) do + encoded_base64_data = extract_base64_data(feed, <<0, 0>>) + "@" <> encoded_base64_data <> ".ed25519" + end + + def decode_generic(<<6, 1, 1>>), do: true + + def decode_generic(<<6, 1, 0>>), do: false + + def decode_generic(<<6, 2>>), do: nil + + def decode_generic(generic) do + tf_tag = binary_part(generic, 0, 2) + decode_generic(generic, tf_tag) + end + + # Matches generic string. + def decode_generic(str, <<6, 0>>) do + [_, str_data] = :binary.split(str, <<6, 0>>) + str_data + end + + # Matches generic bytes. + def decode_generic(bytes, <<6, 3>>), do: bytes + + def decode_msg(msg) do + tf_tag = binary_part(msg, 0, 2) + decode_msg(msg, tf_tag) + end + + # Matches classic message. + def decode_msg(msg, <<1, 0>>) do + encoded_base64_data = extract_base64_data(msg, <<1, 0>>) + "%" <> encoded_base64_data <> ".sha256" + end + + def decode_sig(sig) do + encoded_base64_data = extract_base64_data(sig, <<4, 0>>) + encoded_base64_data <> ".sig.ed25519" + end +end