chiya/lib/chiya_web/references.ex
2023-09-13 23:09:19 +02:00

87 lines
2.1 KiB
Elixir

defmodule ChiyaWeb.References do
@moduledoc """
Finds and replaces references to entities in a string.
A reference is an internal link like the following examples:
* `[[sustainablity]]` -> A note named *Sustainability*
* `[[a-long-unfitting-slug|A simple title]]` -> A note named *A long unfitting title*
"""
@reference_regex ~r/\[\[?(?<id>[\d\w-]+)(?:\|(?<title>[\w\d\s'`äöüß]+))?\]\]/
@doc """
Returns a list of references in `string`.
"""
def get_references(nil), do: []
def get_references(string) do
@reference_regex
|> Regex.scan(string, capture: :all)
|> Enum.map(&map_to_tuple/1)
end
@doc """
Checks a reference returned from `get_references/1` and validates its existence.
"""
def validate_reference({placeholder, slug, custom_title} = _reference) do
note = Chiya.Notes.get_note_by_slug_preloaded(slug)
valid =
case note do
nil -> false
_ -> true
end
# If a custom title was defined, use it,
# otherwise use the note's title
title =
cond do
custom_title != slug ->
custom_title
valid ->
note.name
true ->
slug
end
{placeholder, slug, title, valid}
end
@doc """
Returns a list of slugs that are referenced in `string`.
"""
def get_reference_ids(string) do
string
|> get_references()
|> Enum.map(fn {_, note_slug, _} -> note_slug end)
end
@doc """
Finds and replaces references with the matching url in `string`.
"""
def replace_references(string) do
string
|> get_references()
|> Enum.map(&validate_reference/1)
|> Enum.reduce(string, fn {placeholder, slug, title, valid}, s ->
String.replace(s, placeholder, get_link(slug, title, valid))
end)
end
defp get_link(slug, title, valid) do
"[#{title}](/note/#{slug})#{get_link_class(valid)}"
end
defp get_link_class(valid) do
if valid, do: "", else: "{:.invalid}"
end
defp map_to_tuple([placeholder, note_slug]),
do: {placeholder, note_slug, note_slug}
defp map_to_tuple([placeholder, note_slug, title]),
do: {placeholder, note_slug, title}
end