defmodule ArtNet do
@moduledoc """
Main API for encoding and decoding Art-Net packets.
Use this module when working with complete Art-Net packets, including the
`Art-Net\\0` identifier, OpCode, optional protocol version header, and packet
payload.
```elixir
{:ok, packet} = ArtNet.decode(binary)
{:ok, binary} = ArtNet.encode(packet)
```
Packet structs are defined under `ArtNet.Packet`. Low-level field codecs live
in `ArtNet.Decoder` and `ArtNet.Encoder`.
This library provides encode/decode functionality only. It does not open
sockets or transfer Art-Net packets over the network.
"""
alias ArtNet.{Packet, OpCode}
@artnet_identifier ArtNet.Packet.identifier()
@typedoc """
Any Art-Net packet struct generated by `ArtNet.Packet.Schema`.
"""
@type packet :: struct
@doc """
Decodes a complete Art-Net binary into a packet struct.
Returns `{:ok, packet}` on success or `{:error, %ArtNet.DecodeError{}}` when
the identifier, OpCode, version header, payload, or packet validation fails.
"""
defdelegate decode(data), to: Packet
@doc """
Decodes a complete Art-Net binary into a packet struct.
Raises `ArtNet.DecodeError` when decoding fails.
"""
defdelegate decode!(data), to: Packet
@doc """
Encodes an Art-Net packet struct into a complete Art-Net binary.
Returns `{:ok, binary}` on success or `{:error, %ArtNet.EncodeError{}}` when
the value is not a packet struct, packet validation fails, or any field cannot
be encoded.
"""
defdelegate encode(packet), to: Packet
@doc """
Encodes an Art-Net packet struct into a complete Art-Net binary.
Raises `ArtNet.EncodeError` when encoding fails.
"""
defdelegate encode!(packet), to: Packet
@doc """
Fetches the Art-Net packet opcode from a binary packet.
The return value is `{:ok, op_code}` for a supported OpCode, such as
`{:ok, :op_dmx}`, or `:error` if the binary does not start with the Art-Net
identifier or the OpCode is not supported.
## Examples
iex> ArtNet.fetch_op_code(<<0x41, 0x72, 0x74, 0x2D, 0x4E, 0x65, 0x74, 0x00, 0x00, 0x50, 0x00, 0x0E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF>>)
{:ok, :op_dmx}
iex> ArtNet.fetch_op_code(<<0x41, 0x72, 0x74, 0x2D, 0x4E, 0x65, 0x74, 0x01, 0x00, 0x50, 0x00, 0x0E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF>>)
:error
iex> ArtNet.fetch_op_code(<<0x41, 0x72, 0x74, 0x2D, 0x4E, 0x65, 0x74, 0x00, 0xFF, 0x7F, 0x00, 0x0E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF>>)
:error
"""
@spec fetch_op_code(binary) :: {:ok, OpCode.type()} | :error
def fetch_op_code(<<@artnet_identifier, op_code::little-size(16), _rest::binary>>) do
case OpCode.op_code_type(op_code) do
nil -> :error
op_code -> {:ok, op_code}
end
end
def fetch_op_code(_) do
:error
end
end