defmodule Slime.Parser.EmbeddedEngine do
@moduledoc """
Embedded engine behaviour module.
Provides basic logic of parsing slime with embedded parts for other engines.
"""
alias Slime.Parser.Nodes.EExNode
@type parser_tag :: binary | {binary, Keyword.t()} | %EExNode{}
@type engine_input :: [binary | {:eex, binary}]
@callback render(engine_input, Keyword.t()) :: parser_tag
import Slime.Parser.TextBlock, only: [render_content: 2]
@engines %{
javascript: Slime.Parser.EmbeddedEngine.Javascript,
css: Slime.Parser.EmbeddedEngine.Css,
elixir: Slime.Parser.EmbeddedEngine.Elixir,
eex: Slime.Parser.EmbeddedEngine.EEx
}
|> Map.merge(Application.get_env(:slime, :embedded_engines, %{}))
|> Enum.into(%{}, fn {key, value} -> {to_string(key), value} end)
@registered_engines Map.keys(@engines)
def parse(engine, lines) when engine in @registered_engines do
# NOTE: Add an empty line to keep spaces consistent with verbatim text case
embedded_text = render_content([{0, []} | lines], 0)
{:ok, render_with_engine(engine, embedded_text)}
end
def parse(engine, _) do
{:error, ~s(Unknown embedded engine "#{engine}")}
end
defp render_with_engine(engine, text) do
keep_lines = Application.get_env(:slime, :keep_lines)
text = if keep_lines, do: ["\n" | text], else: text
apply(@engines[engine], :render, [text, [keep_lines: keep_lines]])
end
end
defmodule Slime.Parser.EmbeddedEngine.Javascript do
@moduledoc """
Javascript engine callback module
"""
@behaviour Slime.Parser.EmbeddedEngine
def render(text, _options), do: {"script", children: text}
end
defmodule Slime.Parser.EmbeddedEngine.Css do
@moduledoc """
CSS engine callback module
"""
@behaviour Slime.Parser.EmbeddedEngine
def render(text, _options) do
{"style", attributes: [type: "text/css"], children: text}
end
end
defmodule Slime.Parser.EmbeddedEngine.Elixir do
@moduledoc """
Elixir code engine callback module
"""
@behaviour Slime.Parser.EmbeddedEngine
alias Slime.Parser.Nodes.EExNode
def render(text, options) do
newlines =
if options[:keep_lines] do
count = Enum.count(text, &Kernel.==(&1, "\n"))
[String.duplicate("\n", count)]
else
[]
end
eex =
Enum.map_join(text, fn
{:eex, interpolation} -> ~S"#{" <> interpolation <> "}"
text -> text
end)
%EExNode{content: eex, children: newlines}
end
end
defmodule Slime.Parser.EmbeddedEngine.EEx do
@moduledoc """
EEx engine callback module
"""
@behaviour Slime.Parser.EmbeddedEngine
def render(text, _options), do: text
end