src/erlte.erl

%% Copyright (c) 2022, Madalin Grigore-Enescu <github@ergenius.com>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-module(erlte).
-author("Madalin Grigore-Enescu").

-include("../include/erlte.hrl").

-export([compile/1, compile/2, compile/3]).
-export([compiled_write_file/2, compiled_read_file/1]).
-export([render/1, render/2]).
-export([render_to_compiled/2]).
-export([render_to_file/2, render_to_file/3]).

-export_type([compiled/0, trim_specs/0, tags_specs/0, node_specs/0, format_specs/0]).

-type compiled() :: #erlte_compiled{}.
-type trim_specs() :: #erlte_trim{}.
-type tags_specs() :: #erlte_tags{}.
-type node_specs() :: #erlte_node_specs{}.
-type format_specs() :: #erlte_format_specs{}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% compile
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%   

-spec compile(Input) -> {ok, Compiled} | {error, Error} when
    Input :: binary() | list() | {file, file:name_all()},
    Compiled :: erlte:compiled(),
    Error :: term().
% @doc Compile the specified input with no variables and default format specifications
compile(Input) -> compile(Input, undefined, undefined).

-spec compile(Input, Variables) -> {ok, Compiled} | {error, Error} when
    Input :: binary() | list() | {file, file:name_all()},
    Variables :: list(),    
    Compiled :: erlte:compiled(),
    Error :: term().
% @doc Compile the specified input with variables and default format specifications
compile(Input, Variables) -> compile(Input, Variables, undefined).

-spec compile(Input, Variables, FormatSpecs) -> {ok, Compiled} | {error, Error} when
    Input :: binary() | list() | {file, file:name_all()},
    Variables :: list(),
    FormatSpecs :: undefined | erlte:format_specs(),
    Compiled :: erlte:compiled(),
    Error :: term().
% @doc Compile the specified input according to the format specification
compile(Input, Variables, Options) -> erlte_compiler:compile(Input, Variables, Options).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% compiled
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%   

-spec compiled_write_file(Filename, Compiled) -> ok | {error, Reason} when
    Filename :: file:name_all(),
    Compiled :: erlte:compiled(),
    Reason :: file:posix() | badarg | terminated | system_limit.
% @doc Write the compiled template to the specified file
compiled_write_file(Filename, Compiled = #erlte_compiled{}) -> 
    CompiledBinary = erlang:term_to_binary(Compiled, [{compressed, 9}, {minor_version, 1}]),
    file:write_file(Filename, CompiledBinary).

-spec compiled_read_file(Filename) -> {ok, Compiled} | {error, Reason} when
    Filename :: file:name_all(),
    Compiled :: erlte:compiled(),
    Reason :: file:posix() | badarg | terminated | system_limit.
% @doc Read the compiled template from the specified file
compiled_read_file(Filename) -> 
    case file:read_file(Filename) of
        {ok, Binary} -> 
            try 
                Compiled = erlang:binary_to_term(Binary),
                true = erlang:is_record(Compiled, erlte_compiled),
                {ok, Compiled}
            catch _ -> {error, invalid_file_format}
            end;
        Error -> Error
    end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% render
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 

-spec render(Compiled) -> {ok, Result} | {error, Error} when
    Compiled :: erlte:compiled(),
    Result :: binary(),
    Error :: term().
%% @doc Render the compiled template without replacing any variables
%% There is no error return case now but some maybe added in the future so for compatibility
%% you should handle a generic error case too.
render(Compiled) -> render(Compiled, undefined).

-spec render(Compiled, Variables) -> {ok, Result} | {error, Error} when
    Compiled :: erlte:compiled(),
    Variables :: list(),
    Result :: binary(),
    Error :: term().
% @doc Render compiled template replacing the specified variables
%% There is no error return case now but some maybe added in the future so for compatibility
%% you should handle a generic error case too.
render(Compiled, Variables) -> erlte_renderer:render(Compiled, Variables).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% render_to_compiled
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 

-spec render_to_compiled(Compiled, Variables) -> {ok, NewCompiled} | {error, Error} when
    Compiled :: erlte:compiled(),
    Variables :: list(),
    NewCompiled :: erlte:compiled(),
    Error :: term().
%% @doc Render the compiled template replacing the specified variables and return a new compiled template
%% There is no error return case now but some maybe added in the future so for compatibility
%% reasons you should handle a generic error case too.
render_to_compiled(Compiled, Variables) -> erlte_renderer:render_to_compiled(Compiled, Variables).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% render_to_file
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 

-spec render_to_file(Filename, Compiled) -> {ok, Result} | {error, Reason} when
    Filename :: file:name_all(),
    Compiled :: erlte:compiled(),
    Result :: binary(),
    Reason :: file:posix() | badarg | terminated | system_limit.
% @doc Render compiled template and save the result into the specified file
render_to_file(Filename, Compiled) -> render_to_file(Filename, Compiled, undefined).

-spec render_to_file(Filename, Compiled, Variables) -> {ok, Result} | {error, Reason} when
    Filename :: file:name_all(),
    Compiled :: erlte:compiled(),
    Variables :: undefined | list(),
    Result :: binary(),
    Reason :: file:posix() | badarg | terminated | system_limit.
% @doc Render compiled template replacing the specified variables and save the result into the specified file
render_to_file(Filename, Compiled, Variables) -> erlte_renderer:render_to_file(Filename, Compiled, Variables).