Current section
Files
Jump to
Current section
Files
src/tale@renderer.erl
-module(tale@renderer).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/tale/renderer.gleam").
-export([load_partials/1, render_jobs/4]).
-export_type([pagination_info/0, tag_context/0, render_job/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type pagination_info() :: {pagination_info,
integer(),
integer(),
integer(),
gleam@option:option(binary()),
gleam@option:option(binary())}.
-type tag_context() :: {tag_context,
binary(),
binary(),
binary(),
list(tale@content:page_data())}.
-type render_job() :: {render_job,
tale@content:page_data(),
list(tale@content:page_data()),
gleam@option:option(pagination_info()),
gleam@option:option(tag_context()),
list(tag_context())}.
-file("src/tale/renderer.gleam", 58).
?DOC(
" Partials List. This is a strict list for now.\n"
" TODO: Make partials and generally layouts more flexible.\n"
).
-spec handles_partial_specs() -> list({binary(), binary()}).
handles_partial_specs() ->
[{<<"_partials/head"/utf8>>, <<"head.html"/utf8>>},
{<<"_partials/head_css"/utf8>>, <<"head/css.html"/utf8>>},
{<<"_partials/head_js"/utf8>>, <<"head/js.html"/utf8>>},
{<<"_partials/menu"/utf8>>, <<"menu.html"/utf8>>},
{<<"_partials/header"/utf8>>, <<"header.html"/utf8>>},
{<<"_partials/post_card"/utf8>>, <<"post_card.html"/utf8>>},
{<<"_partials/footer"/utf8>>, <<"footer.html"/utf8>>}].
-file("src/tale/renderer.gleam", 88).
?DOC(" Load handle templates\n").
-spec load_handles_template(binary()) -> {ok, handles:template()} |
{error, binary()}.
load_handles_template(Path) ->
gleam@result:'try'(
begin
_pipe = simplifile:read(Path),
gleam@result:map_error(
_pipe,
fun(Err) ->
<<<<<<"Unable to read template "/utf8, Path/binary>>/binary,
": "/utf8>>/binary,
(simplifile:describe_error(Err))/binary>>
end
)
end,
fun(Template_source) -> _pipe@1 = handles:prepare(Template_source),
gleam@result:map_error(
_pipe@1,
fun(Err@1) ->
<<<<<<"Template syntax error in "/utf8, Path/binary>>/binary,
": "/utf8>>/binary,
(tale@util:describe_tokenizer_error(Err@1))/binary>>
end
) end
).
-file("src/tale/renderer.gleam", 71).
?DOC(" Load templates partials list\n").
-spec load_partial_list(
list({binary(), binary()}),
binary(),
list({binary(), handles:template()})
) -> {ok, list({binary(), handles:template()})} | {error, binary()}.
load_partial_list(Specs, Partials_dir, Acc) ->
case Specs of
[] ->
{ok, lists:reverse(Acc)};
[{Name, File} | Rest] ->
gleam@result:'try'(
load_handles_template(
<<<<Partials_dir/binary, "/"/utf8>>/binary, File/binary>>
),
fun(Template) ->
load_partial_list(
Rest,
Partials_dir,
[{Name, Template} | Acc]
)
end
)
end.
-file("src/tale/renderer.gleam", 50).
?DOC(" Loading template partials\n").
-spec load_partials(binary()) -> {ok, list({binary(), handles:template()})} |
{error, binary()}.
load_partials(Partials_dir) ->
load_partial_list(handles_partial_specs(), Partials_dir, []).
-file("src/tale/renderer.gleam", 239).
?DOC(" Finds the current year. Used in default theme footer.\n").
-spec current_year_string() -> binary().
current_year_string() ->
{Date, _} = begin
_pipe = gleam@time@timestamp:system_time(),
gleam@time@timestamp:to_calendar(_pipe, {duration, 0, 0})
end,
erlang:integer_to_binary(erlang:element(2, Date)).
-file("src/tale/renderer.gleam", 260).
-spec pagination_context(gleam@option:option(pagination_info()), binary()) -> handles@ctx:value().
pagination_context(Info, Base_url) ->
case Info of
none ->
{dict, []};
{some, {pagination_info, Current, Total, Per, Previous, Next}} ->
Abs_previous = gleam@option:map(
Previous,
fun(Link) -> tale@util:absolute_url(Base_url, Link) end
),
Abs_next = gleam@option:map(
Next,
fun(Link@1) -> tale@util:absolute_url(Base_url, Link@1) end
),
{dict,
[{prop, <<"current_page"/utf8>>, {int, Current}},
{prop, <<"total_pages"/utf8>>, {int, Total}},
{prop, <<"per_page"/utf8>>, {int, Per}},
{prop,
<<"previous"/utf8>>,
{str, gleam@option:unwrap(Abs_previous, <<""/utf8>>)}},
{prop,
<<"next"/utf8>>,
{str, gleam@option:unwrap(Abs_next, <<""/utf8>>)}},
{prop,
<<"has_previous"/utf8>>,
{bool, gleam@option:is_some(Abs_previous)}},
{prop,
<<"has_next"/utf8>>,
{bool, gleam@option:is_some(Abs_next)}},
{prop, <<"has_pages"/utf8>>, {bool, Total > 1}}]}
end.
-file("src/tale/renderer.gleam", 339).
-spec tag_permalink_from_slug(binary()) -> binary().
tag_permalink_from_slug(Slug) ->
<<<<"/tags/"/utf8, Slug/binary>>/binary, "/"/utf8>>.
-file("src/tale/renderer.gleam", 329).
-spec tag_value(binary(), binary()) -> handles@ctx:value().
tag_value(Name, Base_url) ->
Slug = tale@util:slugify(Name),
Link = tale@util:absolute_url(Base_url, tag_permalink_from_slug(Slug)),
{dict,
[{prop, <<"name"/utf8>>, {str, Name}},
{prop, <<"slug"/utf8>>, {str, Slug}},
{prop, <<"permalink"/utf8>>, {str, Link}}]}.
-file("src/tale/renderer.gleam", 323).
-spec tag_list_context(list(binary()), binary()) -> handles@ctx:value().
tag_list_context(Tags, Base_url) ->
_pipe = Tags,
_pipe@1 = gleam@list:map(_pipe, fun(Tag) -> tag_value(Tag, Base_url) end),
{list, _pipe@1}.
-file("src/tale/renderer.gleam", 246).
-spec post_summary_to_ctx(tale@content:page_data(), binary()) -> handles@ctx:value().
post_summary_to_ctx(Page, Base_url) ->
{dict,
[{prop,
<<"title"/utf8>>,
{str, erlang:element(2, erlang:element(7, Page))}},
{prop,
<<"description"/utf8>>,
{str, erlang:element(3, erlang:element(7, Page))}},
{prop,
<<"date"/utf8>>,
{str,
gleam@option:unwrap(
erlang:element(4, erlang:element(7, Page)),
<<""/utf8>>
)}},
{prop,
<<"slug"/utf8>>,
{str, erlang:element(8, erlang:element(7, Page))}},
{prop,
<<"permalink"/utf8>>,
{str, tale@util:absolute_url(Base_url, erlang:element(5, Page))}},
{prop,
<<"author"/utf8>>,
{str, erlang:element(7, erlang:element(7, Page))}},
{prop,
<<"draft"/utf8>>,
{bool, erlang:element(5, erlang:element(7, Page))}},
{prop,
<<"has_tags"/utf8>>,
{bool, erlang:element(6, erlang:element(7, Page)) /= []}},
{prop,
<<"tags"/utf8>>,
tag_list_context(
erlang:element(6, erlang:element(7, Page)),
Base_url
)}]}.
-file("src/tale/renderer.gleam", 232).
-spec posts_context(list(tale@content:page_data()), binary()) -> handles@ctx:value().
posts_context(Posts, Base_url) ->
_pipe = Posts,
_pipe@1 = gleam@list:map(
_pipe,
fun(Page) -> post_summary_to_ctx(Page, Base_url) end
),
{list, _pipe@1}.
-file("src/tale/renderer.gleam", 291).
-spec current_tag_context(gleam@option:option(tag_context()), binary()) -> handles@ctx:value().
current_tag_context(Tag, Base_url) ->
case Tag of
none ->
{dict, []};
{some, {tag_context, Name, Slug, Link, Posts}} ->
Absolute_link = tale@util:absolute_url(Base_url, Link),
{dict,
[{prop, <<"name"/utf8>>, {str, Name}},
{prop, <<"slug"/utf8>>, {str, Slug}},
{prop, <<"permalink"/utf8>>, {str, Absolute_link}},
{prop, <<"count"/utf8>>, {int, erlang:length(Posts)}},
{prop, <<"posts"/utf8>>, posts_context(Posts, Base_url)}]}
end.
-file("src/tale/renderer.gleam", 307).
-spec tags_context(list(tag_context()), binary()) -> handles@ctx:value().
tags_context(Tags, Base_url) ->
_pipe = Tags,
_pipe@1 = gleam@list:map(
_pipe,
fun(Tag) ->
{tag_context, Name, Slug, Link, Posts} = Tag,
Absolute_link = tale@util:absolute_url(Base_url, Link),
{dict,
[{prop, <<"name"/utf8>>, {str, Name}},
{prop, <<"slug"/utf8>>, {str, Slug}},
{prop, <<"permalink"/utf8>>, {str, Absolute_link}},
{prop, <<"count"/utf8>>, {int, erlang:length(Posts)}},
{prop, <<"posts"/utf8>>, posts_context(Posts, Base_url)}]}
end
),
{list, _pipe@1}.
-file("src/tale/renderer.gleam", 343).
-spec menu_context(list(tale@config:menu_item()), binary()) -> handles@ctx:value().
menu_context(Items, Base_url) ->
_pipe = Items,
_pipe@1 = gleam@list:map(
_pipe,
fun(Item) ->
{menu_item, Label, Url, _} = Item,
Absolute_url = tale@util:absolute_url(Base_url, Url),
{dict,
[{prop, <<"label"/utf8>>, {str, Label}},
{prop, <<"url"/utf8>>, {str, Absolute_url}}]}
end
),
{list, _pipe@1}.
-file("src/tale/renderer.gleam", 176).
-spec build_page_context(render_job(), tale@config:site_config()) -> handles@ctx:value().
build_page_context(Job, Config) ->
Base_url = erlang:element(6, Config),
Metadata = {dict,
[{prop,
<<"title"/utf8>>,
{str,
erlang:element(2, erlang:element(7, erlang:element(2, Job)))}},
{prop,
<<"description"/utf8>>,
{str,
erlang:element(3, erlang:element(7, erlang:element(2, Job)))}},
{prop,
<<"date"/utf8>>,
{str,
gleam@option:unwrap(
erlang:element(
4,
erlang:element(7, erlang:element(2, Job))
),
<<""/utf8>>
)}},
{prop,
<<"slug"/utf8>>,
{str,
erlang:element(8, erlang:element(7, erlang:element(2, Job)))}},
{prop, <<"site_title"/utf8>>, {str, erlang:element(2, Config)}},
{prop,
<<"site_description"/utf8>>,
{str, erlang:element(4, Config)}},
{prop, <<"site_author"/utf8>>, {str, erlang:element(5, Config)}},
{prop, <<"site_base_url"/utf8>>, {str, Base_url}},
{prop, <<"site_theme"/utf8>>, {str, erlang:element(3, Config)}},
{prop,
<<"site_pagination"/utf8>>,
{int, gleam@option:unwrap(erlang:element(9, Config), 0)}},
{prop,
<<"draft"/utf8>>,
{bool,
erlang:element(5, erlang:element(7, erlang:element(2, Job)))}},
{prop,
<<"author"/utf8>>,
{str,
erlang:element(7, erlang:element(7, erlang:element(2, Job)))}},
{prop,
<<"tags"/utf8>>,
tag_list_context(
erlang:element(6, erlang:element(7, erlang:element(2, Job))),
Base_url
)},
{prop,
<<"permalink"/utf8>>,
{str,
tale@util:absolute_url(
Base_url,
erlang:element(5, erlang:element(2, Job))
)}},
{prop,
<<"is_home"/utf8>>,
{bool,
erlang:element(5, erlang:element(2, Job)) =:= <<"/"/utf8>>}},
{prop,
<<"has_tags"/utf8>>,
{bool,
erlang:element(6, erlang:element(7, erlang:element(2, Job)))
/= []}},
{prop,
<<"site_menu"/utf8>>,
menu_context(erlang:element(10, Config), Base_url)},
{prop,
<<"has_site_menu"/utf8>>,
{bool, erlang:element(10, Config) /= []}}]},
Pagination_prop = {prop,
<<"pagination"/utf8>>,
pagination_context(erlang:element(4, Job), Base_url)},
Tag_prop = {prop,
<<"current_tag"/utf8>>,
current_tag_context(erlang:element(5, Job), Base_url)},
Body_prop = {prop,
<<"body"/utf8>>,
{str, erlang:element(8, erlang:element(2, Job))}},
Base = [{prop, <<"metadata"/utf8>>, Metadata},
Body_prop,
Pagination_prop,
Tag_prop,
{prop,
<<"site_tags"/utf8>>,
tags_context(erlang:element(6, Job), Base_url)},
{prop,
<<"is_home"/utf8>>,
{bool, erlang:element(5, erlang:element(2, Job)) =:= <<"/"/utf8>>}},
{prop,
<<"is_tag_page"/utf8>>,
{bool, gleam@option:is_some(erlang:element(5, Job))}},
{prop,
<<"is_index_page"/utf8>>,
{bool, gleam@option:is_none(erlang:element(5, Job))}},
{prop,
<<"is_subpage"/utf8>>,
{bool, erlang:element(5, erlang:element(2, Job)) /= <<"/"/utf8>>}},
{prop,
<<"has_body"/utf8>>,
{bool, erlang:element(8, erlang:element(2, Job)) /= <<""/utf8>>}},
{prop, <<"current_year"/utf8>>, {str, current_year_string()}}],
case tale@content:is_list_layout(erlang:element(2, Job)) of
true ->
{dict,
[{prop,
<<"pages"/utf8>>,
posts_context(erlang:element(3, Job), Base_url)} |
Base]};
false ->
{dict, Base}
end.
-file("src/tale/renderer.gleam", 380).
-spec add_slot(handles@ctx:value(), binary()) -> {ok, handles@ctx:value()} |
{error, binary()}.
add_slot(Context, Slot) ->
case Context of
{dict, Props} ->
{ok, {dict, [{prop, <<"main"/utf8>>, {str, Slot}} | Props]}};
_ ->
{error,
<<"Unable to extend template context for base layout"/utf8>>}
end.
-file("src/tale/renderer.gleam", 356).
-spec wrap_with_base(
gleam@string_tree:string_tree(),
handles@ctx:value(),
list({binary(), handles:template()}),
binary()
) -> {ok, gleam@string_tree:string_tree()} | {error, binary()}.
wrap_with_base(Inner, Context, Partials, Layouts_dir) ->
Base_path = <<Layouts_dir/binary, "/baseof.html"/utf8>>,
case simplifile_erl:is_file(Base_path) of
{ok, true} ->
gleam@result:'try'(
load_handles_template(Base_path),
fun(Base_template) ->
Slot = unicode:characters_to_binary(Inner),
gleam@result:'try'(
add_slot(Context, Slot),
fun(Extended_context) ->
_pipe = handles:run(
Base_template,
Extended_context,
Partials
),
gleam@result:map_error(
_pipe,
fun(Err) ->
<<<<<<"Template runtime error in baseof for slot "/utf8,
Base_path/binary>>/binary,
": "/utf8>>/binary,
(tale@util:describe_runtime_error(Err))/binary>>
end
)
end
)
end
);
_ ->
{ok, Inner}
end.
-file("src/tale/renderer.gleam", 133).
-spec render_job(
render_job(),
tale@config:site_config(),
list({binary(), handles:template()}),
binary()
) -> {ok, binary()} | {error, binary()}.
render_job(Job, Config, Partials, Layouts_dir) ->
gleam@result:'try'(
load_handles_template(
<<<<<<Layouts_dir/binary, "/"/utf8>>/binary,
(erlang:element(6, erlang:element(2, Job)))/binary>>/binary,
".html"/utf8>>
),
fun(Template) ->
Context = build_page_context(Job, Config),
gleam@result:'try'(
begin
_pipe = handles:run(Template, Context, Partials),
gleam@result:map_error(
_pipe,
fun(Err) ->
<<<<<<<<<<"Template runtime error in "/utf8,
(erlang:element(
6,
erlang:element(2, Job)
))/binary>>/binary,
" for "/utf8>>/binary,
(erlang:element(
3,
erlang:element(2, Job)
))/binary>>/binary,
": "/utf8>>/binary,
(tale@util:describe_runtime_error(Err))/binary>>
end
)
end,
fun(Rendered) ->
gleam@result:'try'(
wrap_with_base(Rendered, Context, Partials, Layouts_dir),
fun(Wrapped) ->
gleam@result:'try'(
tale@paths:ensure_parent_dirs(
erlang:element(4, erlang:element(2, Job))
),
fun(_) ->
_pipe@1 = simplifile:write(
erlang:element(
4,
erlang:element(2, Job)
),
unicode:characters_to_binary(Wrapped)
),
_pipe@2 = gleam@result:map_error(
_pipe@1,
fun(Err@1) ->
<<<<<<"Unable to write "/utf8,
(erlang:element(
4,
erlang:element(
2,
Job
)
))/binary>>/binary,
": "/utf8>>/binary,
(simplifile:describe_error(
Err@1
))/binary>>
end
),
gleam@result:map(
_pipe@2,
fun(_) ->
<<"Wrote "/utf8,
(erlang:element(
4,
erlang:element(2, Job)
))/binary>>
end
)
end
)
end
)
end
)
end
).
-file("src/tale/renderer.gleam", 117).
-spec render_loop(
list(render_job()),
tale@config:site_config(),
list({binary(), handles:template()}),
binary(),
list(binary())
) -> {ok, list(binary())} | {error, binary()}.
render_loop(Jobs, Config, Partials, Layouts_dir, Acc) ->
case Jobs of
[] ->
{ok, lists:reverse(Acc)};
[Job | Rest] ->
gleam@result:'try'(
render_job(Job, Config, Partials, Layouts_dir),
fun(Message) ->
render_loop(
Rest,
Config,
Partials,
Layouts_dir,
[Message | Acc]
)
end
)
end.
-file("src/tale/renderer.gleam", 108).
-spec render_jobs(
list(render_job()),
tale@config:site_config(),
list({binary(), handles:template()}),
binary()
) -> {ok, list(binary())} | {error, binary()}.
render_jobs(Jobs, Config, Partials, Layouts_dir) ->
render_loop(Jobs, Config, Partials, Layouts_dir, []).