update(all): moved codegen to root /codegen
This commit is contained in:
93
codegen/codegen.php
Executable file
93
codegen/codegen.php
Executable file
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
error_reporting(E_ALL^E_NOTICE);
|
||||
|
||||
include("docs/vendor/autoload.php");
|
||||
|
||||
function capitalize($name) {
|
||||
$names = explode("/", $name);
|
||||
return implode("", array_map("ucfirst", $names));
|
||||
}
|
||||
|
||||
function array_change_key_case_recursive($arr) {
|
||||
return array_map(function ($item) {
|
||||
if (is_array($item)) {
|
||||
$item = array_change_key_case_recursive($item);
|
||||
}
|
||||
return $item;
|
||||
}, array_change_key_case($arr));
|
||||
}
|
||||
|
||||
$tpl = new Monotek\MiniTPL\Template;
|
||||
$tpl->set_compile_location("/tmp", true);
|
||||
$tpl->add_default("newline", "\n");
|
||||
|
||||
$api_files = glob("docs/src/spec/*.json");
|
||||
$apis = array_map(function($filename) {
|
||||
return array_change_key_case_recursive(json_decode(file_get_contents($filename), true));
|
||||
}, $api_files);
|
||||
|
||||
usort($apis, function($a, $b) {
|
||||
return strcmp($a['interface'], $b['interface']);
|
||||
});
|
||||
|
||||
foreach (array("structs", "handlers", "interfaces", "request", "") as $type) {
|
||||
foreach ($apis as $api) {
|
||||
if (is_array($api['struct'])) {
|
||||
$name = ucfirst($api['interface']);
|
||||
$filename = str_replace("..", ".", strtolower($name) . "." . $type . ".go");
|
||||
|
||||
$tpl->load("http_$type.tpl");
|
||||
$tpl->assign("parsers", array(
|
||||
"uint64" => "parseUInt64",
|
||||
"bool" => "parseBool",
|
||||
));
|
||||
$tpl->assign("package", $api['package']);
|
||||
$tpl->assign("name", $name);
|
||||
$tpl->assign("self", strtolower(substr($name, 0, 1)));
|
||||
$tpl->assign("api", $api);
|
||||
$tpl->assign("structs", $api['struct']);
|
||||
$imports = array();
|
||||
foreach ($api['struct'] as $struct) {
|
||||
if (isset($struct['imports']))
|
||||
foreach ($struct['imports'] as $import) {
|
||||
$imports[] = $import;
|
||||
}
|
||||
}
|
||||
$tpl->assign("imports", $imports);
|
||||
$tpl->assign("calls", $api['apis']);
|
||||
$contents = str_replace("\n\n}", "\n}", $tpl->get());
|
||||
|
||||
$save = true;
|
||||
if ($type === "" && file_exists($filename)) {
|
||||
$save = false;
|
||||
}
|
||||
if ($save) {
|
||||
file_put_contents($filename, $contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (array("routes") as $type) {
|
||||
$name = ucfirst($api['interface']);
|
||||
$filename = str_replace("..", ".", $type . ".go");
|
||||
|
||||
$tpl->load("http_$type.tpl");
|
||||
$tpl->assign("package", reset($apis)['package']);
|
||||
$tpl->assign("apis", $apis);
|
||||
$contents = $tpl->get();
|
||||
|
||||
file_put_contents($filename, $contents);
|
||||
}
|
||||
|
||||
// camel case to snake case
|
||||
function decamel($input) {
|
||||
preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches);
|
||||
$ret = $matches[0];
|
||||
foreach ($ret as &$match) {
|
||||
$match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match);
|
||||
}
|
||||
return implode('_', $ret);
|
||||
}
|
||||
14
codegen/templates/http_.tpl
Normal file
14
codegen/templates/http_.tpl
Normal file
@@ -0,0 +1,14 @@
|
||||
package {package}
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var _ = errors.Wrap
|
||||
|
||||
{foreach $calls as $call}
|
||||
func (*{name}) {call.name|capitalize}(r *{name|lcfirst}{call.name|capitalize}Request) (interface{}, error) {
|
||||
return nil, errors.New("Not implemented: {name}.{call.name}")
|
||||
}
|
||||
|
||||
{/foreach}
|
||||
16
codegen/templates/http_handlers.tpl
Normal file
16
codegen/templates/http_handlers.tpl
Normal file
@@ -0,0 +1,16 @@
|
||||
package {package}
|
||||
|
||||
{load warning.tpl}
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/titpetric/factory/resputil"
|
||||
)
|
||||
|
||||
{foreach $calls as $call}
|
||||
func ({self}h *{name}Handlers) {call.name|capitalize}(w http.ResponseWriter, r *http.Request) {
|
||||
params := {name|lcfirst}{call.name|capitalize}Request{}.new()
|
||||
resputil.JSON(w, params.Fill(r), func() (interface{}, error) { return {self}h.{name}.{call.name|capitalize}(params) })
|
||||
}
|
||||
{/foreach}
|
||||
39
codegen/templates/http_interfaces.tpl
Normal file
39
codegen/templates/http_interfaces.tpl
Normal file
@@ -0,0 +1,39 @@
|
||||
package {package}
|
||||
|
||||
{load warning.tpl}
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// HTTP handlers are a superset of internal APIs
|
||||
type {name}Handlers struct {
|
||||
*{name}
|
||||
}
|
||||
|
||||
func ({name}Handlers) new() *{name}Handlers {
|
||||
return &{name}Handlers{
|
||||
{name}{}.New(),
|
||||
}
|
||||
}
|
||||
|
||||
// Internal API interface
|
||||
type {name}API interface {
|
||||
{foreach $calls as $call}
|
||||
{call.name|capitalize}(*{name|lcfirst}{call.name|capitalize}Request) (interface{}, error)
|
||||
{/foreach}
|
||||
}
|
||||
|
||||
// HTTP API interface
|
||||
type {name}HandlersAPI interface {
|
||||
{foreach $calls as $call}
|
||||
{call.name|capitalize}(http.ResponseWriter, *http.Request)
|
||||
{/foreach}
|
||||
|
||||
// Authenticate API requests
|
||||
Authenticator() func(http.Handler) http.Handler
|
||||
}
|
||||
|
||||
// Compile time check to see if we implement the interfaces
|
||||
var _ {name}HandlersAPI = &{name}Handlers{}
|
||||
var _ {name}API = &{name}{}
|
||||
51
codegen/templates/http_request.tpl
Normal file
51
codegen/templates/http_request.tpl
Normal file
@@ -0,0 +1,51 @@
|
||||
package {package}
|
||||
|
||||
{load warning.tpl}
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
var _ = chi.URLParam
|
||||
|
||||
{foreach $calls as $call}
|
||||
// {name} {call.name} request parameters
|
||||
type {name|lcfirst}{call.name|capitalize}Request struct {
|
||||
{foreach $call.parameters as $params}
|
||||
{foreach $params as $method => $param}
|
||||
{param.name} {param.type}{newline}
|
||||
{/foreach}
|
||||
{/foreach}
|
||||
}
|
||||
|
||||
func ({name|lcfirst}{call.name|capitalize}Request) new() *{name|lcfirst}{call.name|capitalize}Request {
|
||||
return &{name|lcfirst}{call.name|capitalize}Request{}
|
||||
}
|
||||
|
||||
func ({self} *{name|lcfirst}{call.name|capitalize}Request) Fill(r *http.Request) error {
|
||||
r.ParseForm()
|
||||
get := map[string]string{}
|
||||
post := map[string]string{}
|
||||
urlQuery := r.URL.Query()
|
||||
for name, param := range urlQuery {
|
||||
get[name] = string(param[0])
|
||||
}
|
||||
postVars := r.Form
|
||||
for name, param := range postVars {
|
||||
post[name] = string(param[0])
|
||||
}
|
||||
{foreach $call.parameters as $method => $params}
|
||||
{foreach $params as $param}
|
||||
{if strtolower($method) === "path"}
|
||||
{self}.{param.name} = {if ($param.type !== "string")}{$parsers[$param.type]}({/if}chi.URLParam(r, "{param.name}"){if ($param.type !== "string")}){/if}{newline}
|
||||
{elseif substr($param.type, 0, 2) !== '[]'}
|
||||
{self}.{param.name} = {if ($param.type !== "string")}{$parsers[$param.type]}({method|strtolower}["{param.name}"]){else}{method|strtolower}["{param.name}"]{/if}{newline}
|
||||
{/if}
|
||||
{/foreach}
|
||||
{/foreach}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ RequestFiller = {name|lcfirst}{call.name|capitalize}Request{}.new()
|
||||
{/foreach}
|
||||
42
codegen/templates/http_routes.tpl
Normal file
42
codegen/templates/http_routes.tpl
Normal file
@@ -0,0 +1,42 @@
|
||||
package {package}
|
||||
|
||||
{load warning.tpl}
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"reflect"
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
func MountRoutes(r chi.Router) {
|
||||
{foreach $apis as $api}
|
||||
{api.interface|strtolower} := {api.interface|capitalize}Handlers{}.new()
|
||||
{/foreach}
|
||||
{foreach $apis as $api}
|
||||
r.Group(func (r chi.Router) {
|
||||
r.Use({api.interface|strtolower}.Authenticator())
|
||||
r.Route("{api.path}", func(r chi.Router) {
|
||||
{foreach $api.apis as $call}
|
||||
r.{eval echo capitalize(strtolower($call.method))}("{call.path}", {api.interface|strtolower}.{call.name|capitalize})
|
||||
{/foreach}
|
||||
})
|
||||
})
|
||||
{/foreach}
|
||||
|
||||
var printRoutes func(chi.Routes, string, string)
|
||||
printRoutes = func(r chi.Routes, indent string, prefix string) {
|
||||
routes := r.Routes()
|
||||
for _, route := range routes {
|
||||
if route.SubRoutes != nil && len(route.SubRoutes.Routes()) > 0 {
|
||||
fmt.Printf(indent+"%s - with %d handlers, %d subroutes\n", route.Pattern, len(route.Handlers), len(route.SubRoutes.Routes()))
|
||||
printRoutes(route.SubRoutes, indent+"\t", prefix+route.Pattern[:len(route.Pattern)-2])
|
||||
} else {
|
||||
for key, fn := range route.Handlers {
|
||||
fmt.Printf("%s%s\t%s -> %s\n", indent, key, prefix+route.Pattern, runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
printRoutes(r, "", "")
|
||||
}
|
||||
58
codegen/templates/http_structs.tpl
Normal file
58
codegen/templates/http_structs.tpl
Normal file
@@ -0,0 +1,58 @@
|
||||
package {package}
|
||||
|
||||
{load warning.tpl}
|
||||
|
||||
{if !empty($imports)}
|
||||
import (
|
||||
{foreach ($imports as $import)}
|
||||
"{import}"
|
||||
{/foreach}
|
||||
)
|
||||
{/if}
|
||||
|
||||
type ({foreach $structs as $struct}
|
||||
|
||||
{if strpos($name, "Literal") !== false}
|
||||
{foreach $struct.fields as $field}
|
||||
{field}{newline}
|
||||
{/foreach}
|
||||
{else}
|
||||
// {api.title}
|
||||
{struct.name} struct {
|
||||
{foreach $struct.fields as $field}
|
||||
{field.name} {field.type} `{if $field.tag}{$field.tag} {/if}db:"{if $field.dbname}{$field.dbname}{else}{$field.name|decamel}{/if}"`{newline}
|
||||
{/foreach}
|
||||
|
||||
changed []string
|
||||
}
|
||||
|
||||
{/if}{/foreach}
|
||||
)
|
||||
|
||||
/* Constructors */
|
||||
{foreach $structs as $struct}
|
||||
func ({struct.name}) New() *{struct.name} {
|
||||
return &{struct.name}{}
|
||||
}
|
||||
{/foreach}
|
||||
|
||||
/* Getters/setters */
|
||||
{foreach $structs as $struct}
|
||||
{foreach $struct.fields as $field}
|
||||
func ({self} *{struct.name}) Get{field.name}() {field.type} {
|
||||
return {self}.{field.name}
|
||||
}
|
||||
|
||||
func ({self} *{struct.name}) Set{field.name}(value {field.type}) *{struct.name} {{if !$field.complex}
|
||||
if {self}.{field.name} != value {
|
||||
{self}.changed = append({self}.changed, "{field.name}")
|
||||
{self}.{field.name} = value
|
||||
}
|
||||
{else}
|
||||
{self}.{field.name} = value
|
||||
{/if}
|
||||
return {self}
|
||||
}
|
||||
{/foreach}
|
||||
|
||||
{/foreach}
|
||||
14
codegen/templates/warning.tpl
Normal file
14
codegen/templates/warning.tpl
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
Hello! This file is auto-generated from `docs/src/spec.json`.
|
||||
|
||||
For development:
|
||||
In order to update the generated files, edit this file under the location,
|
||||
add your struct fields, imports, API definitions and whatever you want, and:
|
||||
|
||||
1. run [spec](https://github.com/titpetric/spec) in the same folder,
|
||||
2. run `./_gen.php` in this folder.
|
||||
|
||||
You may edit `{name|strtolower}.go`, `{name|strtolower}.util.go` or `{name|strtolower}_test.go` to
|
||||
implement your API calls, helper functions and tests. The file `{name|strtolower}.go`
|
||||
is only generated the first time, and will not be overwritten if it exists.
|
||||
*/
|
||||
Reference in New Issue
Block a user