106 lines
2.5 KiB
Elixir
106 lines
2.5 KiB
Elixir
defmodule Frenzy.Pipeline.RenderGeminiStage do
|
|
require Logger
|
|
alias Frenzy.Pipeline.Stage
|
|
@behaviour Stage
|
|
|
|
@impl Stage
|
|
def apply(_opts, %{content: content, content_type: "text/gemini"} = item_params) do
|
|
html = render_gemini(content)
|
|
{:ok, %{item_params | content_type: "text/html", content: html}}
|
|
end
|
|
|
|
def apply(_opts, %{content_type: content_type} = item_params) do
|
|
Logger.debug("Not rendering Gemini text for item, incorect content type: #{content_type}")
|
|
{:ok, item_params}
|
|
end
|
|
|
|
@impl Stage
|
|
def validate_opts(opts) do
|
|
{:ok, opts}
|
|
end
|
|
|
|
@impl Stage
|
|
def default_opts(), do: %{}
|
|
|
|
def render_gemini(gemini_source) do
|
|
gemini_source
|
|
|> Gemini.parse()
|
|
|> render_lines()
|
|
|> Floki.raw_html()
|
|
end
|
|
|
|
@spec render_lines([Gemini.line()], [String.t()]) :: [String.t()]
|
|
|
|
defp render_lines(lines, acc \\ [])
|
|
|
|
defp render_lines([], acc) do
|
|
Enum.reverse(acc)
|
|
end
|
|
|
|
defp render_lines([{:text, text} | rest], acc) do
|
|
render_lines(rest, [{"p", [], [text]} | acc])
|
|
end
|
|
|
|
defp render_lines([{:link, uri, text} | rest], acc) do
|
|
uri_str = URI.to_string(uri)
|
|
text = if is_nil(text), do: uri_str, else: text
|
|
a = {"a", [{"href", uri_str}], [text]}
|
|
p = {"p", [], [a]}
|
|
render_lines(rest, [p | acc])
|
|
end
|
|
|
|
defp render_lines([{:preformatted_start, _alt} | rest], acc) do
|
|
{preformatted_lines, [:preformatted_end, rest]} =
|
|
Enum.split_while(rest, fn
|
|
{:preformatted_text, _} -> true
|
|
_ -> false
|
|
end)
|
|
|
|
pre_text =
|
|
preformatted_lines
|
|
|> Enum.map(fn {:preformatted_text, text} -> text end)
|
|
|> Enum.join("\n")
|
|
|
|
pre = {"pre", [], pre_text}
|
|
render_lines(rest, [pre | acc])
|
|
end
|
|
|
|
defp render_lines([{:heading, text, level} | rest], acc) do
|
|
tag = "h#{level}"
|
|
heading = {tag, [], [text]}
|
|
render_lines(rest, [heading | acc])
|
|
end
|
|
|
|
defp render_lines([{:list_item, _text} | _rest] = lines, acc) do
|
|
{list_items, rest} =
|
|
Enum.split_while(lines, fn
|
|
{:list_item, _} -> true
|
|
_ -> false
|
|
end)
|
|
|
|
lis =
|
|
Enum.map(list_items, fn {:list_item, text} ->
|
|
{"li", [], [text]}
|
|
end)
|
|
|
|
ul = {"ul", [], lis}
|
|
render_lines(rest, [ul | acc])
|
|
end
|
|
|
|
defp render_lines([{:quoted, _text} | _rest] = lines, acc) do
|
|
{quoted_lines, rest} =
|
|
Enum.split_while(lines, fn
|
|
{:quoted, _} -> true
|
|
_ -> false
|
|
end)
|
|
|
|
ps =
|
|
Enum.map(quoted_lines, fn {:quoted, text} ->
|
|
{"p", [], [text]}
|
|
end)
|
|
|
|
blockquote = {"blockquote", [], ps}
|
|
render_lines(rest, [blockquote | acc])
|
|
end
|
|
end
|