-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( <<<>/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", 233). ?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", 254). -spec pagination_context(gleam@option:option(pagination_info())) -> handles@ctx:value(). pagination_context(Info) -> case Info of none -> {dict, []}; {some, {pagination_info, Current, Total, Per, Previous, Next}} -> {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(Previous, <<""/utf8>>)}}, {prop, <<"next"/utf8>>, {str, gleam@option:unwrap(Next, <<""/utf8>>)}}, {prop, <<"has_previous"/utf8>>, {bool, gleam@option:is_some(Previous)}}, {prop, <<"has_next"/utf8>>, {bool, gleam@option:is_some(Next)}}, {prop, <<"has_pages"/utf8>>, {bool, Total > 1}}]} end. -file("src/tale/renderer.gleam", 321). -spec tag_permalink_from_slug(binary()) -> binary(). tag_permalink_from_slug(Slug) -> <<<<"/tags/"/utf8, Slug/binary>>/binary, "/"/utf8>>. -file("src/tale/renderer.gleam", 312). -spec tag_value(binary()) -> handles@ctx:value(). tag_value(Name) -> Slug = tale@util:slugify(Name), {dict, [{prop, <<"name"/utf8>>, {str, Name}}, {prop, <<"slug"/utf8>>, {str, Slug}}, {prop, <<"permalink"/utf8>>, {str, tag_permalink_from_slug(Slug)}}]}. -file("src/tale/renderer.gleam", 306). -spec tag_list_context(list(binary())) -> handles@ctx:value(). tag_list_context(Tags) -> _pipe = Tags, _pipe@1 = gleam@list:map(_pipe, fun tag_value/1), {list, _pipe@1}. -file("src/tale/renderer.gleam", 240). -spec post_summary_to_ctx(tale@content:page_data()) -> handles@ctx:value(). post_summary_to_ctx(Page) -> {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, 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)))}]}. -file("src/tale/renderer.gleam", 226). -spec posts_context(list(tale@content:page_data())) -> handles@ctx:value(). posts_context(Posts) -> _pipe = Posts, _pipe@1 = gleam@list:map(_pipe, fun post_summary_to_ctx/1), {list, _pipe@1}. -file("src/tale/renderer.gleam", 277). -spec current_tag_context(gleam@option:option(tag_context())) -> handles@ctx:value(). current_tag_context(Tag) -> case Tag of none -> {dict, []}; {some, {tag_context, Name, Slug, Link, Posts}} -> {dict, [{prop, <<"name"/utf8>>, {str, Name}}, {prop, <<"slug"/utf8>>, {str, Slug}}, {prop, <<"permalink"/utf8>>, {str, Link}}, {prop, <<"count"/utf8>>, {int, erlang:length(Posts)}}, {prop, <<"posts"/utf8>>, posts_context(Posts)}]} end. -file("src/tale/renderer.gleam", 291). -spec tags_context(list(tag_context())) -> handles@ctx:value(). tags_context(Tags) -> _pipe = Tags, _pipe@1 = gleam@list:map( _pipe, fun(Tag) -> {tag_context, Name, Slug, Link, Posts} = Tag, {dict, [{prop, <<"name"/utf8>>, {str, Name}}, {prop, <<"slug"/utf8>>, {str, Slug}}, {prop, <<"permalink"/utf8>>, {str, Link}}, {prop, <<"count"/utf8>>, {int, erlang:length(Posts)}}, {prop, <<"posts"/utf8>>, posts_context(Posts)}]} end ), {list, _pipe@1}. -file("src/tale/renderer.gleam", 325). -spec menu_context(list(tale@config:menu_item())) -> handles@ctx:value(). menu_context(Items) -> _pipe = Items, _pipe@1 = gleam@list:map( _pipe, fun(Item) -> {menu_item, Label, Url, _} = Item, {dict, [{prop, <<"label"/utf8>>, {str, Label}}, {prop, <<"url"/utf8>>, {str, 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) -> 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, erlang:element(6, Config)}}, {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))) )}, {prop, <<"permalink"/utf8>>, {str, 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))}, {prop, <<"has_site_menu"/utf8>>, {bool, erlang:element(10, Config) /= []}}]}, Pagination_prop = {prop, <<"pagination"/utf8>>, pagination_context(erlang:element(4, Job))}, Tag_prop = {prop, <<"current_tag"/utf8>>, current_tag_context(erlang:element(5, Job))}, 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))}, {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]}; false -> {dict, Base} end. -file("src/tale/renderer.gleam", 361). -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", 337). -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 = <>, 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( <<<<<>/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, []).