63 lines
1.4 KiB
Elixir
63 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
|
|
{
|
|
:ok,
|
|
acc
|
|
|> Enum.reverse()
|
|
|> :erlang.list_to_binary(),
|
|
rest
|
|
}
|
|
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
|