func (this *Inertia) Version(version func() string) {
	this.version = version
}
Set a version callback "func() string"
package inertia
import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"github.com/elipzis/inertia-echo/util"
	_ "github.com/joho/godotenv/autoload"
	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"reflect"
	"sync"
)
const HeaderPrefix = "X-Inertia"
const HeaderVersion = HeaderPrefix + "-Version"
const HeaderLocation = HeaderPrefix + "-Location"
const HeaderPartialData = HeaderPrefix + "-Partial-Data"
type Inertia struct {
	config InertiaConfig
	templates *template.Template
	sharedProps      map[string]map[string]interface{}
	sharedPropsMutex *sync.Mutex
	version          interface{}
}
type InertiaConfig struct {
	Echo *echo.Echo
	PublicPath       string
	TemplatesPath    string
	RootView         string
	TemplateFuncMap  template.FuncMap
	HTTPErrorHandler echo.HTTPErrorHandler
	RequestIDConfig  middleware.RequestIDConfig
}
var DefaultHTTPErrorHandler = func(err error, c echo.Context) {
	code := http.StatusInternalServerError
	if he, ok := err.(*echo.HTTPError); ok {
		code = he.Code
	}
	_ = c.Render(code, "Error", map[string]interface{}{
		"status": code,
	})
}
func NewDefaultInertiaConfig(e *echo.Echo) (this InertiaConfig) {
	this = InertiaConfig{}
	this.Echo = e
	this.RootView = util.GetEnvOrDefault("INERTIA_ROOT_VIEW", "app.html")
	this.PublicPath = util.GetEnvOrDefault("INERTIA_PUBLIC_PATH", "public")
	this.TemplatesPath = util.GetEnvOrDefault("INERTIA_RESOURCES_PATH", "resources") + "/views/*.html"
	this.HTTPErrorHandler = DefaultHTTPErrorHandler
	this.TemplateFuncMap = template.FuncMap{
		"inertia":         util.Inertia,
		"json_encode":     util.JsonEncode,
		"json_encode_raw": util.JsonEncodeRaw,
		"mix":             util.Mix,
		"routes": func() template.JS {
			retVal, _ := json.Marshal(e.Routes())
			return template.JS(string(retVal))
		},
		"routes_ziggy": func(v interface{}) template.HTML {
			ziggy := util.NewZiggy(e, v.(map[string]interface{}))
			retVal, _ := json.Marshal(ziggy)
			return template.HTML(fmt.Sprintf("<script>var Ziggy = %s;</script>", string(retVal)))
		},
	}
	this.RequestIDConfig = middleware.DefaultRequestIDConfig
	return this
}
func NewInertia(echo *echo.Echo) (this *Inertia) {
	return NewInertiaWithConfig(NewDefaultInertiaConfig(echo))
}
func NewInertiaWithConfig(config InertiaConfig) (this *Inertia) {
	if config.Echo == nil {
		log.Fatal("[Inertia] echo.Echo reference required in the given config!")
	}
	this = new(Inertia)
	this.config = config
	this.sharedProps = make(map[string]map[string]interface{})
	this.sharedPropsMutex = &sync.Mutex{}
	this.config.Echo.Renderer = this
	this.config.Echo.HTTPErrorHandler = this.config.HTTPErrorHandler
	log.Printf("[Inertia] Loading templates out of %s", this.config.TemplatesPath)
	this.templates = template.Must(template.New("").Funcs(this.config.TemplateFuncMap).ParseGlob(this.config.TemplatesPath))
	this.SetMixVersion()
	this.config.Echo.Use(middleware.RequestIDWithConfig(this.config.RequestIDConfig))
	return this
}
func (this *Inertia) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
	sharedProps := this.Shared(c)
	isMap := reflect.TypeOf(data).Kind() == reflect.Map
	if this.templates.Lookup(name) != nil {
		if isMap {
			viewContext := data.(map[string]interface{})
			viewContext["reverse"] = c.Echo().Reverse
			viewContext["shared"] = sharedProps
		}
		return this.templates.ExecuteTemplate(w, name, data)
	}
	if isMap {
		if sharedProps != nil {
			return NewResponse(name, util.MergeMaps(sharedProps, data.(map[string]interface{})), this.config.RootView, this.GetVersion()).Status(c.Response().Status).ToResponse(c)
		} else {
			return NewResponse(name, data.(map[string]interface{}), this.config.RootView, this.GetVersion()).Status(c.Response().Status).ToResponse(c)
		}
	}
	return NewResponse(name, sharedProps, this.config.RootView, this.GetVersion()).Status(c.Response().Status).ToResponse(c)
}
func (this *Inertia) Share(c echo.Context, key string, value interface{}) {
	rid := c.Request().Header.Get(echo.HeaderXRequestID)
	this.sharedPropsMutex.Lock()
	if reqSharedProps, ok := this.sharedProps[rid]; ok {
		reqSharedProps[key] = value
	} else {
		this.sharedProps[rid] = map[string]interface{}{
			key: value,
		}
	}
	this.sharedPropsMutex.Unlock()
}
func (this *Inertia) Shares(c echo.Context, values map[string]interface{}) {
	rid := c.Request().Header.Get(echo.HeaderXRequestID)
	this.sharedPropsMutex.Lock()
	if _, ok := this.sharedProps[rid]; !ok {
		this.sharedProps[rid] = make(map[string]interface{})
	}
	this.sharedPropsMutex.Unlock()
	for key, value := range values {
		this.sharedPropsMutex.Lock()
		this.sharedProps[rid][key] = value
		this.sharedPropsMutex.Unlock()
	}
}
func (this *Inertia) GetShared(c echo.Context, key string) (interface{}, bool) {
	rid := c.Request().Header.Get(echo.HeaderXRequestID)
	this.sharedPropsMutex.Lock()
	if reqSharedProps, ok := this.sharedProps[rid]; ok {
		value, ok := reqSharedProps[key]
		this.sharedPropsMutex.Unlock()
		return value, ok
	}
	return nil, false
}
func (this *Inertia) Shared(c echo.Context) map[string]interface{} {
	rid := c.Request().Header.Get(echo.HeaderXRequestID)
	this.sharedPropsMutex.Lock()
	sharedProps := this.sharedProps[rid]
	delete(this.sharedProps, rid)
	this.sharedPropsMutex.Unlock()
	return sharedProps
}