lib/bcs.ex

defmodule Bcs do
  @moduledoc """
  BCS format.
  """

  @doc """
  Encode an BCS struct.
  """
  @spec encode(value :: struct()) :: {:ok, binary()} | :error
  def encode(%module{} = value) do
    {:ok, Bcs.Encoder.encode(value, module)}
  rescue _ ->
    :error
  end

  @doc """
  Same as encode/1, but raises ArgumentError.
  """
  @spec encode!(value :: struct()) :: binary()
  def encode!(%module{} = value) do
    Bcs.Encoder.encode(value, module)
  end

  @doc """
  Encode an BCS value to type.
  """
  @spec encode(value :: term(), type :: term()) :: {:ok, binary()} | :error
  def encode(value, type) do
    {:ok, Bcs.Encoder.encode(value, type)}
  rescue _ ->
    :error
  end

  @doc """
  Same as encode/2, but raises ArgumentError.
  """
  @spec encode!(value :: term(), type :: term()) :: binary()
  defdelegate encode!(value, type), to: Bcs.Encoder, as: :encode

  @doc """
  Decode bytes as BCS type.
  """
  @spec decode(bytes :: binary(), type :: term()) :: {:ok, term()} | :error
  defdelegate decode(bytes, type), to: Bcs.Decoder

  @doc """
  Same as decode/2, but raises ArgumentError.
  """
  @spec decode!(bytes :: binary(), type :: term()) :: term()
  def decode!(bytes, type) do
    case Bcs.Decoder.decode(bytes, type) do
      {:ok, value} ->
        value
      :error ->
        raise ArgumentError, "Can't decode #{inspect(bytes)} as #{inspect(type)}"
    end
  end
end