/*
 Author: José Bollo <jobol@nonadev.net>

 https://gitlab.com/jobol/mustach

 SPDX-License-Identifier: ISC
*/

#ifndef _mustach_h_included_
#define _mustach_h_included_

struct mustach_sbuf; /* see below */

/**
 * Current version of mustach and its derivates
 */
#define MUSTACH_VERSION 102
#define MUSTACH_VERSION_MAJOR (MUSTACH_VERSION / 100)
#define MUSTACH_VERSION_MINOR (MUSTACH_VERSION % 100)

/**
 * Maximum nested section supported
 */
#define MUSTACH_MAX_DEPTH  256

/**
 * Maximum nested template supported
 */
#define MUSTACH_MAX_NESTING  64

/**
 * Maximum length of tags in mustaches {{...}}
 */
#define MUSTACH_MAX_LENGTH 4096

/**
 * Maximum length of delimitors (2 normally but extended here)
 */
#define MUSTACH_MAX_DELIM_LENGTH 8

/**
 * Flags specific to mustach core
 */
#define Mustach_With_NoExtensions   0
#define Mustach_With_Colon          1
#define Mustach_With_EmptyTag       2
#define Mustach_With_AllExtensions  3

/*
 * Definition of error codes returned by mustach
 */
#define MUSTACH_OK                       0
#define MUSTACH_ERROR_SYSTEM            -1
#define MUSTACH_ERROR_UNEXPECTED_END    -2
#define MUSTACH_ERROR_EMPTY_TAG         -3
#define MUSTACH_ERROR_TAG_TOO_LONG      -4
#define MUSTACH_ERROR_BAD_SEPARATORS    -5
#define MUSTACH_ERROR_TOO_DEEP          -6
#define MUSTACH_ERROR_CLOSING           -7
#define MUSTACH_ERROR_BAD_UNESCAPE_TAG  -8
#define MUSTACH_ERROR_INVALID_ITF       -9
#define MUSTACH_ERROR_ITEM_NOT_FOUND    -10
#define MUSTACH_ERROR_PARTIAL_NOT_FOUND -11
#define MUSTACH_ERROR_UNDEFINED_TAG     -12
#define MUSTACH_ERROR_TOO_MUCH_NESTING  -13

/*
 * You can use definition below for user specific error
 *
 * The macro MUSTACH_ERROR_USER is involutive so for any value
 *   value = MUSTACH_ERROR_USER(MUSTACH_ERROR_USER(value))
 */
#define MUSTACH_ERROR_USER_BASE         -100
#define MUSTACH_ERROR_USER(x)           (MUSTACH_ERROR_USER_BASE-(x))
#define MUSTACH_IS_ERROR_USER(x)        (MUSTACH_ERROR_USER(x) >= 0)

/**
 * mustach_itf - pure abstract mustach - interface for callbacks
 *
 * The functions enter and next should return 0 or 1.
 *
 * All other functions should normally return MUSTACH_OK (zero).
 *
 * If any function returns a negative value, it means an error that
 * stop the processing and that is reported to the caller. Mustach
 * also has its own error codes. Using the macros MUSTACH_ERROR_USER
 * and MUSTACH_IS_ERROR_USER could help to avoid clashes.
 *
 * @start: If defined (can be NULL), starts the mustach processing
 *         of the closure, called at the very beginning before any
 *         mustach processing occurs.
 *
 * @put: If defined (can be NULL), writes the value of 'name'
 *       to 'file' with 'escape' or not.
 *       As an extension (see NO_ALLOW_EMPTY_TAG), the 'name' can be
 *       the empty string. In that later case an implementation can
 *       return MUSTACH_ERROR_EMPTY_TAG to refuse empty names.
 *       If NULL and 'get' NULL the error MUSTACH_ERROR_INVALID_ITF
 *       is returned.
 *
 * @enter: Enters the section of 'name' if possible.
 *         Musts return 1 if entered or 0 if not entered.
 *         When 1 is returned, the function 'leave' will always be called.
 *         Conversely 'leave' is never called when enter returns 0 or
 *         a negative value.
 *         When 1 is returned, the function must activate the first
 *         item of the section.
 *
 * @next: Activates the next item of the section if it exists.
 *        Musts return 1 when the next item is activated.
 *        Musts return 0 when there is no item to activate.
 *
 * @leave: Leaves the last entered section
 *
 * @partial: If defined (can be NULL), returns in 'sbuf' the content of the
 *           partial of 'name'. @see mustach_sbuf
 *           If NULL but 'get' not NULL, 'get' is used instead of partial.
 *           If NULL and 'get' NULL and 'put' not NULL, 'put' is called with
 *           a true FILE.
 *
 * @emit: If defined (can be NULL), writes the 'buffer' of 'size' with 'escape'.
 *        If NULL the standard function 'fwrite' is used with a true FILE.
 *        If not NULL that function is called instead of 'fwrite' to output
 *        text.
 *        It implies that if you define either 'partial' or 'get' callback,
 *        the meaning of 'FILE *file' is abstract for mustach's process and
 *        then you can use 'FILE*file' pass any kind of pointer (including NULL)
 *        to the function 'fmustach'. An example of a such behaviour is given by
 *        the implementation of 'mustach_json_c_write'.
 *
 * @get: If defined (can be NULL), returns in 'sbuf' the value of 'name'.
 *       As an extension (see NO_ALLOW_EMPTY_TAG), the 'name' can be
 *       the empty string. In that later case an implementation can
 *       return MUSTACH_ERROR_EMPTY_TAG to refuse empty names.
 *       If 'get' is NULL and 'put' NULL the error MUSTACH_ERROR_INVALID_ITF
 *       is returned.
 *
 * @stop: If defined (can be NULL), stops the mustach processing
 *        of the closure, called at the very end after all mustach
 *        processing occurered. The status returned by the processing
 *        is passed to the stop.
 *
 * The array below summarize status of callbacks:
 *
 *    FULLY OPTIONAL:   start partial
 *    MANDATORY:        enter next leave
 *    COMBINATORIAL:    put emit get
 *
 * Not definig a MANDATORY callback returns error MUSTACH_ERROR_INVALID_ITF.
 *
 * For COMBINATORIAL callbacks the array below summarize possible combinations:
 *
 *  combination  : put     : emit    : get     : abstract FILE
 *  -------------+---------+---------+---------+-----------------------
 *  HISTORIC     : defined : NULL    : NULL    : NO: standard FILE
 *  MINIMAL      : NULL    : NULL    : defined : NO: standard FILE
 *  CUSTOM       : NULL    : defined : defined : YES: abstract FILE
 *  DUCK         : defined : NULL    : defined : NO: standard FILE
 *  DANGEROUS    : defined : defined : any     : YES or NO, depends on 'partial'
 *  INVALID      : NULL    : any     : NULL    : -
 *
 * The DUCK case runs on one leg. 'get' is not used if 'partial' is defined
 * but is used for 'partial' if 'partial' is NULL. Thus for clarity, do not use
 * it that way but define 'partial' and let 'get' be NULL.
 *
 * The DANGEROUS case is special: it allows abstract FILE if 'partial' is defined
 * but forbids abstract FILE when 'partial' is NULL.
 *
 * The INVALID case returns error MUSTACH_ERROR_INVALID_ITF.
 */
struct mustach_itf {
	int (*start)(void *closure);
	int (*put)(void *closure, const char *name, int escape, FILE *file);
	int (*enter)(void *closure, const char *name);
	int (*next)(void *closure);
	int (*leave)(void *closure);
	int (*partial)(void *closure, const char *name, struct mustach_sbuf *sbuf);
	int (*emit)(void *closure, const char *buffer, size_t size, int escape, FILE *file);
	int (*get)(void *closure, const char *name, struct mustach_sbuf *sbuf);
	void (*stop)(void *closure, int status);
};

/**
 * mustach_sbuf - Interface for handling zero terminated strings
 *
 * That structure is used for returning zero terminated strings -in 'value'-
 * to mustach. The callee can provide a function for releasing the returned
 * 'value'. Three methods for releasing the string are possible.
 *
 *  1. no release: set either 'freecb' or 'releasecb' with NULL (done by default)
 *  2. release without closure: set 'freecb' to its expected value
 *  3. release with closure: set 'releasecb' and 'closure' to their expected values
 *
 * @value: The value of the string. That value is not changed by mustach -const-.
 *
 * @freecb: The function to call for freeing the value without closure.
 *          For convenience, signature of that callback is compatible with 'free'.
 *          Can be NULL.
 *
 * @releasecb: The function to release with closure.
 *             Can be NULL.
 *
 * @closure: The closure to use for 'releasecb'.
 *
 * @length: Length of the value or zero if unknown and value null terminated.
 *          To return the empty string, let it to zero and let value to NULL.
 */
struct mustach_sbuf {
	const char *value;
	union {
		void (*freecb)(void*);
		void (*releasecb)(const char *value, void *closure);
	};
	void *closure;
	size_t length;
};

/**
 * mustach_file - Renders the mustache 'template' in 'file' for 'itf' and 'closure'.
 *
 * @template: the template string to instantiate
 * @length:   length of the template or zero if unknown and template null terminated
 * @itf:      the interface to the functions that mustach calls
 * @closure:  the closure to pass to functions called
 * @file:     the file where to write the result
 *
 * Returns 0 in case of success, -1 with errno set in case of system error
 * a other negative value in case of error.
 */
extern int mustach_file(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, FILE *file);

/**
 * mustach_fd - Renders the mustache 'template' in 'fd' for 'itf' and 'closure'.
 *
 * @template: the template string to instantiate
 * @length:   length of the template or zero if unknown and template null terminated
 * @itf:      the interface to the functions that mustach calls
 * @closure:  the closure to pass to functions called
 * @fd:       the file descriptor number where to write the result
 *
 * Returns 0 in case of success, -1 with errno set in case of system error
 * a other negative value in case of error.
 */
extern int mustach_fd(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, int fd);

/**
 * mustach_mem - Renders the mustache 'template' in 'result' for 'itf' and 'closure'.
 *
 * @template: the template string to instantiate
 * @length:   length of the template or zero if unknown and template null terminated
 * @itf:      the interface to the functions that mustach calls
 * @closure:  the closure to pass to functions called
 * @result:   the pointer receiving the result when 0 is returned
 * @size:     the size of the returned result
 *
 * Returns 0 in case of success, -1 with errno set in case of system error
 * a other negative value in case of error.
 */
extern int mustach_mem(const char *template, size_t length, const struct mustach_itf *itf, void *closure, int flags, char **result, size_t *size);

/***************************************************************************
* compatibility with version before 1.0
*/
#ifdef __GNUC__
#define DEPRECATED_MUSTACH(func) func __attribute__ ((deprecated))
#elif defined(_MSC_VER)
#define DEPRECATED_MUSTACH(func) __declspec(deprecated) func
#elif !defined(DEPRECATED_MUSTACH)
#pragma message("WARNING: You need to implement DEPRECATED_MUSTACH for this compiler")
#define DEPRECATED_MUSTACH(func) func
#endif
/**
 * OBSOLETE use mustach_file
 *
 * fmustach - Renders the mustache 'template' in 'file' for 'itf' and 'closure'.
 *
 * @template: the template string to instantiate, null terminated
 * @itf:      the interface to the functions that mustach calls
 * @closure:  the closure to pass to functions called
 * @file:     the file where to write the result
 *
 * Returns 0 in case of success, -1 with errno set in case of system error
 * a other negative value in case of error.
 */
DEPRECATED_MUSTACH(extern int fmustach(const char *template, const struct mustach_itf *itf, void *closure, FILE *file));

/**
 * OBSOLETE use mustach_fd
 *
 * fdmustach - Renders the mustache 'template' in 'fd' for 'itf' and 'closure'.
 *
 * @template: the template string to instantiate, null terminated
 * @itf:      the interface to the functions that mustach calls
 * @closure:  the closure to pass to functions called
 * @fd:       the file descriptor number where to write the result
 *
 * Returns 0 in case of success, -1 with errno set in case of system error
 * a other negative value in case of error.
 */
DEPRECATED_MUSTACH(extern int fdmustach(const char *template, const struct mustach_itf *itf, void *closure, int fd));

/**
 * OBSOLETE use mustach_mem
 *
 * mustach - Renders the mustache 'template' in 'result' for 'itf' and 'closure'.
 *
 * @template: the template string to instantiate, null terminated
 * @itf:      the interface to the functions that mustach calls
 * @closure:  the closure to pass to functions called
 * @result:   the pointer receiving the result when 0 is returned
 * @size:     the size of the returned result
 *
 * Returns 0 in case of success, -1 with errno set in case of system error
 * a other negative value in case of error.
 */
DEPRECATED_MUSTACH(extern int mustach(const char *template, const struct mustach_itf *itf, void *closure, char **result, size_t *size));

#endif

