gemini-ex/lib/gemini/response.ex

70 lines
1.4 KiB
Elixir

defmodule Gemini.Response do
@moduledoc """
A response to a Gemini protocol request.
"""
@enforce_keys [:status, :meta]
defstruct [:status, :meta, :body]
@type t :: %__MODULE__{
status: integer(),
meta: String.t(),
body: nil | binary()
}
@spec parse(data :: binary()) :: {:ok, t()} | {:error, term()}
@doc """
Parse a Gemini response from the given data.
"""
def parse(<<status::binary-size(2), " ", rest::binary>>) do
status = String.to_integer(status)
case parse_meta(rest) do
{:error, reason} ->
{:error, reason}
{:ok, meta, body} ->
{
:ok,
%__MODULE__{
status: status,
meta: meta,
body: body
}
}
end
end
def parse(_) do
{:error, "Expected Gemini response to begin with two digit status code and space"}
end
defp parse_meta(data, acc \\ [], length \\ 0)
defp parse_meta(<<"\r\n", rest::binary>>, acc, _length) do
body =
if rest == "" do
nil
else
rest
end
{
:ok,
acc
|> Enum.reverse()
|> :erlang.list_to_binary(),
body
}
end
defp parse_meta(_data, _acc, 1024) do
{:error, "Expected meta string in Gemini response to be no longer than 1024 bytes"}
end
defp parse_meta(<<c::binary-size(1), rest::binary>>, acc, length) do
parse_meta(rest, [c | acc], length + 1)
end
end