3
0

update(all): moved codegen to root /codegen

This commit is contained in:
Tit Petric
2018-07-13 17:47:05 +02:00
parent b65a9ab396
commit 837c10e448
19 changed files with 12 additions and 331 deletions

93
codegen/codegen.php Executable file
View 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);
}

View 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}

View 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}

View 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}{}

View 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}

View 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, "", "")
}

View 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}

View 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.
*/