defmodule Blogit.Meta do alias Blogit.GitRepository @posts_folder Application.get_env(:blogit, :posts_folder, ".") @meta_divider Application.get_env(:blogit, :meta_divider, "<><><><><><><><>") defstruct [ :created_at, :updated_at, :author, :title, :category, :tags, :published, :title_image_path, :year, :month, :pinned ] def from_file_name(file_name, repository, raw, name) do meta_file_name = file_name |> String.replace_suffix(".md", ".yml") meta_path = GitRepository.local_path |> Path.join(@posts_folder) |> Path.join("meta") |> Path.join(meta_file_name) create_meta(File.read(meta_path), file_name, repository, raw, name) end def folder, do: Path.join(@posts_folder, "meta") |> String.trim_leading("/") defp create_meta({:error, _}, file_name, repository, raw, name) do create_from_map( merge_with_inline(%{}, raw), file_name, repository, raw, name ) end defp create_meta({:ok, data}, file_name, repository, raw, name) do meta = merge_with_inline(YamlElixir.read_from_string(data), raw) create_from_map(meta, file_name, repository, raw, name) end defp merge_with_inline(data, raw) when is_map(data) do merge_with_inline(data, raw, String.contains?(raw, @meta_divider)) end defp merge_with_inline(_, raw) do merge_with_inline(%{}, raw, String.contains?(raw, @meta_divider)) end defp merge_with_inline(data, _, false), do: data defp merge_with_inline(data, raw, true) do [raw_meta | _] = String.split(raw, @meta_divider, trim: true) |> Enum.map(&String.trim/1) merge_meta(data, YamlElixir.read_from_string(raw_meta)) end defp merge_meta(current, inline) when is_map(inline) do Map.merge(current, inline) end defp merge_meta(current, _), do: current defp create_from_map( data, file_name, repository, raw, name ) when is_map(data) do created_at = data["created_at"] || GitRepository.file_created_at( repository, Path.join(@posts_folder, file_name) ) updated_at = data["updated_at"] || GitRepository.file_updated_at( repository, Path.join(@posts_folder, file_name) ) author = data["author"] || GitRepository.file_author( repository, Path.join(@posts_folder, file_name) ) {:ok, created_at, _} = Calendar.NaiveDateTime.Parse.iso8601(created_at) {:ok, updated_at, _} = Calendar.NaiveDateTime.Parse.iso8601(updated_at) %__MODULE__{ created_at: created_at, updated_at: updated_at, author: author, title: data["title"] || retrieve_title(raw, name), tags: Map.get(data, "tags", []) |> Enum.map(&Kernel.to_string/1), published: Map.get(data, "published", true), category: data["category"], year: Integer.to_string(created_at.year), month: Integer.to_string(created_at.month), title_image_path: data["title_image_path"], pinned: data["pinned"] || false } end defp create_from_map(_, file_name, repository, raw, name) do create_from_map(%{}, file_name, repository, raw, name) end defp retrieve_title(raw, name) do guess_title(Regex.named_captures(~r/^\s*\#\s*(?