Current section
Files
Jump to
Current section
Files
src/emo.erl
%% -------------------------------------------------------------------
%% @doc
%% Erlang Mustache renderer
%% Note: Doesnt yet support inverted section, tripple curlies or &,
%% nor partials or set delimiters.
%% @copyright (C) 2013, Christoffer Vikström (chvi77@gamil.com)
%% -------------------------------------------------------------------
-module(emo).
-export([render/2]).
%% -------------------------------------------------------------------
-type template() :: string()|binary().
-type context() :: [{atom(), iodata()}]|map().
-spec render(Template::template(), Context::context()) -> binary().
render(Template, Context) when is_binary(Template) ->
render(binary_to_list(Template), Context);
render(Template, Context) ->
iolist_to_binary(render(Template, Context, [])).
%% -------------------------------------------------------------------
render([${,${|Rest], Context, Acc) ->
{Tag, NewRest} = find_end(Rest),
{FinalRest, NewAcc} = handle_tag(Tag, NewRest, Context),
render(FinalRest, Context, [NewAcc|Acc]);
render([C|Rest], Context, Acc) ->
render(Rest, Context, [C|Acc]);
render([], _, Acc) -> lists:reverse(Acc).
find_end(List) -> find_end(List, []).
find_end([$},$}|Rest], Tag) -> {lists:reverse(Tag), Rest};
find_end([C|Rest], Acc) -> find_end(Rest, [C|Acc]).
handle_tag([$!|_], Rest, _) -> {Rest, []};
handle_tag([$#|Tag], Rest, Context) ->
{Section, NewRest} = find_end_section(Tag, Rest),
SubContext = lookup(Tag, Context),
{NewRest, render_section(Section, Context, SubContext)};
handle_tag([$^|Tag], Rest, Context) ->
{Section, NewRest} = find_end_section(Tag, Rest),
case lookup(Tag, Context) of
<<"">> ->
{NewRest, render_section(Section, Context, true)};
_ ->
{NewRest, <<"">>}
end;
handle_tag(Tag, Rest, Context) ->
case lookup(Tag, Context) of
<<"">> ->
{Rest, <<"">>};
Value ->
{Rest, Value}
end.
find_end_section(Tag, Rest) -> find_end_section(Tag, Rest, []).
find_end_section(Tag, [${,${,$/|Rest], Acc) ->
case find_end(Rest) of
{Tag, NewRest} ->
{lists:reverse(Acc), NewRest};
{_, NewRest} ->
find_end_section(Tag, NewRest)
end;
find_end_section(Tag, [C|Rest], Acc) ->
find_end_section(Tag, Rest, [C|Acc]).
render_section(_, _, SubContext) when is_binary(SubContext) -> SubContext;
render_section(Section, Context, true) -> render(Section, Context);
render_section(_, _, false) -> <<"">>;
render_section(_, _, []) -> <<"">>;
render_section(Section, _, SubContext) when is_list(SubContext) ->
[ render(Section, C) || C <- SubContext ];
render_section(Section, Context, SubContext) when is_function(SubContext) ->
SubContext(Section, Context).
lookup(Tag, Context) when is_map(Context) ->
case maps:find(list_to_atom(Tag), Context) of
error ->
<<"">>;
{ok, Value} ->
Value
end;
lookup(Tag, Context) when is_list(Context) ->
case lists:keyfind(list_to_atom(Tag), 1, Context) of
false ->
<<"">>;
{_, Value} ->
Value
end.