001package io.jstach.jstachio.context;
002
003import io.jstach.jstache.JStacheConfig;
004import io.jstach.jstachio.Output;
005import io.jstach.jstachio.Output.EncodedOutput;
006import io.jstach.jstachio.Template;
007
008/**
009 * A context aware template.
010 *
011 * @param <T> model type
012 */
013public interface ContextTemplate<T> {
014
015        /**
016         * Renders the passed in model to an appendable like output.
017         * @param <A> output type
018         * @param <E> error type
019         * @param model a model assumed never to be <code>null</code>.
020         * @param context context node.
021         * @param appendable the appendable to write to.
022         * @return the passed in appendable for convenience
023         * @throws E if there is an error writing to the output
024         * @apiNote if the eventual output is to be bytes use
025         * {@link #write(Object, ContextNode, io.jstach.jstachio.Output.EncodedOutput)} as it
026         * will leverage pre-encoding if the template has it.
027         */
028        public <A extends Output<E>, E extends Exception> A execute( //
029                        T model, //
030                        ContextNode context, //
031                        A appendable) throws E;
032
033        /**
034         * Renders the passed in model directly to a binary stream leveraging pre-encoded
035         * parts of the template. This <em>may</em> improve performance when rendering UTF-8
036         * to an OutputStream as some of the encoding is done in advance. Because the encoding
037         * is done statically you cannot pass the charset in. The chosen charset comes from
038         * {@link JStacheConfig#charset()}.
039         * @param <A> output type
040         * @param <E> error type
041         * @param model a model assumed never to be <code>null</code>.
042         * @param context context node.
043         * @param output to write to.
044         * @return the passed in output for convenience
045         * @throws E if an error occurs while writing to output
046         */
047        public <A extends io.jstach.jstachio.Output.EncodedOutput<E>, E extends Exception> A write( //
048                        T model, //
049                        ContextNode context, //
050                        A output) throws E;
051
052        /**
053         * Creates a context template from a regular template if is not already a context
054         * template.
055         * @param <T> model type
056         * @param template the template to be wrapped
057         * @return context template
058         */
059        @SuppressWarnings({ "null", "unchecked" })
060        public static <T> ContextTemplate<T> of(Template<T> template) {
061                if (template instanceof ContextTemplate ct) {
062                        return ct;
063                }
064                return new DecoratedContextTemplate<>(template);
065        }
066
067}
068
069record DecoratedContextTemplate<T> (Template<T> template) implements ContextTemplate<T> {
070
071        @Override
072        public <A extends Output<E>, E extends Exception> A execute(T model, ContextNode context, A appendable) throws E {
073                var out = ContextAwareOutput.of(appendable, context);
074                template.execute(model, out);
075                return appendable;
076        }
077
078        @Override
079        public <A extends EncodedOutput<E>, E extends Exception> A write(T model, ContextNode context, A output) throws E {
080                var out = ContextAwareOutput.of(output, context);
081                template.write(model, out);
082                return output;
083        }
084
085}