diff options
Diffstat (limited to 'lib/irc/parser/line.ex')
-rw-r--r-- | lib/irc/parser/line.ex | 125 |
1 files changed, 37 insertions, 88 deletions
diff --git a/lib/irc/parser/line.ex b/lib/irc/parser/line.ex index 48ddd9c..b436c57 100644 --- a/lib/irc/parser/line.ex +++ b/lib/irc/parser/line.ex @@ -2,93 +2,58 @@ defmodule Irc.Parser.Line do @moduledoc """ IRC line parser/encoder """ + alias Irc.Line + require Logger - @type t :: %__MODULE__{ - tags: Map.t(), - source: Irc.Mask.t() | String.t() | nil, - command: String.t(), - args: list() - } - defstruct __private__: %{}, tags: %{}, source: nil, command: nil, args: [] - - @spec parse(string) :: t() + @spec parse(string) :: Line.t() @doc "Parse a server-to-client line." - def parse(line) do + def parse(line, owner_module \\ nil, owner \\ nil) + + def parse(line, context, _) when is_map(context) do + module = Irc.Context.module(context) + pid = Irc.Context.pid(context) + parse(line) + |> set_owner(module, pid) + end + + def parse(line, owner_module, owner) do {tags, rest} = parse_tags(line) - {source, rest} = parse_source(rest) + {source, rest} = parse_source(rest, owner_module, owner) {command, rest} = parse_cmd(rest) args = parse_args(rest) private = %{ + owner: owner, + owner_module: owner_module, at: DateTime.utc_now() } - %__MODULE__{__private__: private, tags: tags, source: source, command: command, args: args} + %Line{__private__: private, tags: tags, source: source, command: command, args: args} end - @spec encode(t()) :: String.t + @spec set_owner(Line.t(), module(), pid()) :: Line.t() + def set_owner(line = %Line{__private__: private = %{owner: nil, owner_module: nil}}, module, owner) when is_atom(module) and is_pid(owner) do + private = Map.put(private, :owner_module, module) + |> Map.put(:owner, owner) + %Line{line | __private__: private} + end + + def set_owner(line = %Line{__private__: private = %{owner: opid, owner_module: omod}}, module, owner) when is_atom(module) and is_pid(owner) do + Logger.debug("Parser.Line: tried to re-own an already owned line #{inspect {{omod,opid},{module,owner}}}") + line + end + + @spec encode(Line.t()) :: String.t @doc "Encode a line" - def encode(line = %__MODULE__{}, conn \\ %Irc.Connection{}) do + def encode(line = %Line{}, conn \\ %Irc.Connection{}) do [line.source, line.tags, line.command, line.args] src = if line.source, do: ":"<>to_string(line.source) tags = Enum.reduce(line.tags, [], fn({k,v}, acc) -> ["#{k}=#{v}" | acc] end) |> Enum.join(";") - tags = if Enum.member?(conn.capabs || [], "message-tags") && tags != "", do: "@"<>tags + tags = if Irc.Context.capab?(conn, "message-tags") && tags != "", do: "@"<>tags [tags, src, line.command, encode_args(line.args)] |> Enum.filter(fn(x) -> x end) |> Enum.join(" ") end - def new(command) do - new(command, []) - end - - def new(command, args) do - new(command, args, %{}) - end - - def new(command, arg, tags) when not is_list(arg) do - new(command, [arg], tags) - end - - def new(command, args, tags) do - %__MODULE__{command: command, args: args, tags: tags, __private__: %{at: DateTime.utc_now()}} - end - - @doc "Returns the line date (server time if sent, otherwise, parse time)" - @spec at(t()) :: DateTime.t() - def at(line = %__MODULE__{__private__: %{at: at}, tags: %{"time" => server_time}}) do - case DateTime.from_iso8601(server_time) do - {:ok, date} -> date - _ -> at - end - end - def at(%__MODULE__{__private__: %{at: at}}), do: at - def at(_), do: nil - - @spec to?(t(), Irc.Connection.t() | Irc.Mask.t() | Irc.User.t()) :: boolean - @doc "Returns true if the line is adressed to the connection." - def to?(line = %__MODULE__{args: [nick | _]}, target = %{__struct__: s, nick: nick}) when s in [Irc.Connection, Irc.Mask, Irc.User] do - true - end - def to?(%__MODULE__{}, %{__struct__: s}) when s in [Irc.Connection, Irc.Mask, Irc.User], do: false - - @spec self?(t(), Irc.Connection.t() | Irc.Mask.t() | Irc.User.t()) :: boolean - @doc "Returns true if the line source is the from the given connection/mask." - def self?(line = %__MODULE__{source: %Irc.Mask{nick: nick}}, target = %Irc.Connection{nick: nick}) do - true - end - def self?(%__MODULE__{source: mask = %Irc.Mask{}}, mask = %Irc.Mask{}) do - true - end - def self?(line = %__MODULE__{source: mask = %Irc.Mask{}}, user = %Irc.User{}) do - self?(line, Irc.User.to_mask(user)) - end - def self?(%__MODULE__{source: nick}, %Irc.Connection{nick: nick}) do - true - end - def self?(%__MODULE__{}, %Irc.Connection{}), do: false - def self?(%__MODULE__{}, %Irc.User{}), do: false - def self?(%__MODULE__{}, %Irc.Mask{}), do: false - # ARGS defp parse_args(input), do: parse_args(input, []) @@ -153,18 +118,18 @@ defmodule Irc.Parser.Line do # SOURCE - defp parse_source([?: | input]), do: parse_source(input, []) - defp parse_source(input), do: {nil, input} + defp parse_source([?: | input], m, p), do: parse_source(input, [], m, p) + defp parse_source(input, _, _), do: {nil, input} - defp parse_source([0x20 | rest], acc) do + defp parse_source([0x20 | rest], acc, m, p) do string = acc_to_str(acc) - result = case Irc.Mask.parse(string) do + result = case Irc.Mask.parse(string, m, p) do {:error, _} -> string {:ok, mask} -> mask end {result, rest} end - defp parse_source([char | rest], acc), do: parse_source(rest, [char | acc]) + defp parse_source([char | rest], acc, m, p), do: parse_source(rest, [char | acc], m, p) # TAGS @@ -236,20 +201,4 @@ defmodule Irc.Parser.Line do defp encode_args([], acc), do: acc - defimpl String.Chars, for: __MODULE__ do - def to_string(line) do - Irc.Parser.Line.encode(line) - end - end - - defimpl Inspect, for: __MODULE__ do - @moduledoc false - import Inspect.Algebra - - def inspect(struct, _opts) do - tags = Enum.map(struct.tags, fn({k, v}) -> concat([k, "=", v]) end) |> Enum.join(",") - Enum.join(["#IRC.Line<", struct.source, " ", struct.command, " ", inspect(struct.args), " (", tags, ")>"], "") - end - end - end |