Packages
gnat
1.1.3
1.16.0
1.15.2
1.15.1
1.15.0
1.14.1
1.14.0
1.14.0-rc1
1.14.0-rc0
1.13.1
1.13.0
1.12.1
1.12.0
1.11.1
1.11.0
1.10.2
1.10.2-rc1
1.10.2-rc0
1.10.1
1.10.0
1.9.1
1.9.0
1.8.5
1.8.4
1.8.3
1.8.2
1.8.1
1.7.1
1.7.0
1.6.0
1.5.2
1.5.1
1.5.0
1.4.0
1.3.0
1.3.0-rc0
1.2.1
1.2.0
1.1.3
1.1.2
1.1.1
1.1.0
1.0.1
1.0.0
0.7.0
0.6.1
0.6.0
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.2
0.4.1
0.4.0
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
A nats client in pure elixir. Resilience, Performance, Ease-of-Use.
Current section
Files
Jump to
Current section
Files
lib/gnat/parsec.ex
defmodule Gnat.Parsec do
defstruct partial: nil
import NimbleParsec
subject = ascii_string([?!..?~], min: 1)
length = integer(min: 1)
sid = integer(min: 1)
whitespace = ascii_char([32, ?\t])
|> times(min: 1)
op_msg = ascii_char([?m,?M])
|> ascii_char([?s,?S])
|> ascii_char([?g,?G])
op_err = ascii_char([?-])
|> ascii_char([?e,?E])
|> ascii_char([?r,?R])
|> ascii_char([?r,?R])
op_info = ascii_char([?i,?I])
|> ascii_char([?n,?N])
|> ascii_char([?f,?F])
|> ascii_char([?o,?O])
op_ping = ascii_char([?p,?P])
|> ascii_char([?i,?I])
|> ascii_char([?n,?N])
|> ascii_char([?g,?G])
op_pong = ascii_char([?p,?P])
|> ascii_char([?o,?O])
|> ascii_char([?n,?N])
|> ascii_char([?g,?G])
op_ok = ascii_char([?+])
|> ascii_char([?o,?O])
|> ascii_char([?k,?K])
err = replace(op_err, :err)
|> ignore(whitespace)
|> ignore(string("'"))
|> optional(utf8_string([not: ?'], min: 1))
|> ignore(string("'\r\n"))
msg = replace(op_msg, :msg)
|> ignore(whitespace)
|> concat(subject)
|> ignore(whitespace)
|> concat(sid)
|> ignore(whitespace)
|> choice([
subject |> ignore(whitespace) |> concat(length),
length
])
|> ignore(string("\r\n"))
ok = replace(op_ok |> string("\r\n"), :ok)
ping = replace(op_ping |> string("\r\n"), :ping)
pong = replace(op_pong |> string("\r\n"), :pong)
info = replace(op_info, :info)
|> ignore(whitespace)
|> utf8_string([not: ?\r], min: 2)
|> ignore(string("\r\n"))
defparsecp :command, choice([msg, ok, ping, pong, info, err])
def new, do: %__MODULE__{}
def parse(%__MODULE__{partial: nil}=state, string) do
{partial, commands} = parse_commands(string, [])
{%{state | partial: partial}, commands}
end
def parse(%__MODULE__{partial: partial}=state, string) do
{partial, commands} = parse_commands(partial <> string, [])
{%{state | partial: partial}, commands}
end
def parse_commands("", list), do: {nil, Enum.reverse(list)}
def parse_commands(str, list) do
case parse_command(str) do
{:ok, command, rest} -> parse_commands(rest, [command | list])
{:error, partial} -> {partial, Enum.reverse(list)}
end
end
@spec parse_command(binary()) :: {:ok, tuple(), binary()} | {:error, binary()}
def parse_command(string) do
case command(string) do
{:ok, [:msg, subject, sid, length], rest, _, _, _} ->
finish_msg(subject, sid, nil, length, rest, string)
{:ok, [:msg, subject, sid, reply_to, length], rest, _, _, _} ->
finish_msg(subject, sid, reply_to, length, rest, string)
{:ok, [atom], rest, _, _, _} ->
{:ok, atom, rest}
{:ok, [:info, json], rest, _, _, _} ->
{:ok, {:info, Jason.decode!(json, keys: :atoms)}, rest}
{:ok, [:err, msg], rest, _, _, _} ->
{:ok, {:error, msg}, rest}
{:error, _, _, _, _, _} ->
{:error, string}
end
end
def finish_msg(subject, sid, reply_to, length, rest, string) do
case rest do
<< body::size(length)-binary, "\r\n", rest::binary>> ->
{:ok, {:msg, subject, sid, reply_to, body}, rest}
_other ->
{:error, string}
end
end
end