- ChangelogParser: parses priv/static/changelog.txt into structured entries - ChangelogLive: timeline view with DaisyUI styling - Route at /changelog in authenticated scope - What's New link in org dropdown menu
48 lines
1.5 KiB
Elixir
48 lines
1.5 KiB
Elixir
defmodule ToweropsWeb.ChangelogParser do
|
|
@moduledoc "Parses priv/static/changelog.txt into structured entries."
|
|
|
|
@doc "Returns a list of %{date: Date.t(), title: String.t(), items: [String.t()]}"
|
|
def parse do
|
|
path = Application.app_dir(:towerops, "priv/static/changelog.txt")
|
|
|
|
case File.read(path) do
|
|
{:ok, content} -> parse_content(content)
|
|
{:error, _} -> []
|
|
end
|
|
end
|
|
|
|
defp parse_content(content) do
|
|
content
|
|
|> String.split("\n")
|
|
|> Enum.reduce([], fn line, acc ->
|
|
cond do
|
|
# Date header with title: "2026-02-13 — Title"
|
|
Regex.match?(~r/^\d{4}-\d{2}-\d{2}\s*[—–-]\s*.+/, line) ->
|
|
[date_str, title] = String.split(line, ~r/\s*[—–-]\s*/, parts: 2)
|
|
entry = %{date: Date.from_iso8601!(String.trim(date_str)), title: String.trim(title), items: []}
|
|
[entry | acc]
|
|
|
|
# Date header without title: "2026-02-13"
|
|
Regex.match?(~r/^\d{4}-\d{2}-\d{2}\s*$/, line) ->
|
|
date = Date.from_iso8601!(String.trim(line))
|
|
entry = %{date: date, title: nil, items: []}
|
|
[entry | acc]
|
|
|
|
# Bullet point
|
|
String.starts_with?(String.trim(line), "* ") ->
|
|
case acc do
|
|
[current | rest] ->
|
|
item = String.trim(line) |> String.trim_leading("* ")
|
|
[%{current | items: current.items ++ [item]} | rest]
|
|
|
|
[] ->
|
|
acc
|
|
end
|
|
|
|
true ->
|
|
acc
|
|
end
|
|
end)
|
|
|> Enum.reverse()
|
|
end
|
|
end
|