code generator for mock API server

master
Brad Rydzewski 7 years ago
parent 278b70fc3b
commit 950c2ad3c3

@ -0,0 +1,174 @@
package main
import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"log"
"net/textproto"
"os"
"regexp"
"strconv"
"strings"
"github.com/bmatcuk/doublestar"
"github.com/urfave/cli"
"github.com/bradrydzewski/togo/template"
)
type (
httptestParams struct {
Package string
Routes []*httptestRoute
}
httptestRoute struct {
Source string
Method string
Path string
Body string
Status int
Header map[string]string
}
)
var httptestCommand = cli.Command{
Name: "httptest",
Usage: "generate httptest server",
Action: httptestAction,
Flags: []cli.Flag{
cli.StringFlag{
Name: "package",
Value: "testdata",
},
cli.StringFlag{
Name: "input",
Value: "files/**",
},
cli.StringFlag{
Name: "output",
Value: "testdata_gen.go",
},
cli.StringFlag{
Name: "exclude",
},
},
}
func httptestAction(c *cli.Context) error {
pattern := c.Args().First()
if pattern == "" {
pattern = c.String("input")
}
matches, err := doublestar.Glob(pattern)
if err != nil {
return err
}
var exclude *regexp.Regexp
if s := c.String("exclude"); s != "" {
exclude = regexp.MustCompilePOSIX(s)
}
params := httptestParams{
Package: c.String("package"),
}
for _, match := range matches {
stat, oserr := os.Stat(match)
if oserr != nil {
return oserr
}
if stat.IsDir() {
continue
}
if exclude != nil && exclude.MatchString(match) {
continue
}
log.Printf("parsing file %s", match)
raw, ioerr := ioutil.ReadFile(match)
if ioerr != nil {
return ioerr
}
route, parseErr := parseRoute(raw)
if parseErr != nil {
return parseErr
}
route.Source = strings.TrimPrefix(match, "files/")
params.Routes = append(params.Routes, route)
}
wr := os.Stdout
if output := c.String("output"); output != "" {
wr, err = os.Create(output)
if err != nil {
return err
}
defer wr.Close()
}
return template.Execute(wr, "httptest.tmpl", &params)
}
func parseRoute(in []byte) (*httptestRoute, error) {
out := new(httptestRoute)
out.Header = map[string]string{}
buf := bufio.NewReader(bytes.NewBuffer(in))
r := textproto.NewReader(buf)
//
// parses the method and path
//
line, err := r.ReadLine()
if err != nil {
return nil, err
}
parts := strings.Split(line, " ")
if len(parts) != 2 {
return nil, fmt.Errorf("Invalid request line. Want <method> <path>.")
}
out.Method = parts[0]
out.Path = parts[1]
//
// parses the mime headers
//
header, err := r.ReadMIMEHeader()
if err != nil {
fmt.Println(err)
}
for k, v := range header {
out.Header[k] = strings.Join(v, "; ")
}
//
// extracts the response status code
//
out.Status, err = strconv.Atoi(header.Get("Status"))
if err != nil {
return nil, fmt.Errorf("Invalid Status code. %s", err)
}
delete(out.Header, "Status")
//
// parse the remainder of the file as the body
//
body, err := ioutil.ReadAll(buf)
if err != nil {
return nil, err
}
out.Body = string(body)
return out, nil
}

@ -17,6 +17,7 @@ func main() {
ddlCommand,
sqlCommand,
httpCommand,
httptestCommand,
tmplCommand,
i18nCommand,
}

@ -0,0 +1,40 @@
GET /user
Status: 200
Date: Mon, 01 Jul 2013 17:27:06 GMT
Content-Type: application/json; charset=utf-8
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 56
X-RateLimit-Reset: 1372700873
{
"login": "octocat",
"id": 1,
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false,
"name": "monalisa octocat",
"company": "GitHub",
"blog": "https://github.com/blog",
"location": "San Francisco",
"email": "octocat@github.com",
"hireable": false,
"bio": "There once was...",
"public_repos": 2,
"public_gists": 1,
"followers": 20,
"following": 0,
"created_at": "2008-01-14T04:33:35Z",
"updated_at": "2008-01-14T04:33:35Z"
}

@ -0,0 +1,52 @@
PATCH /user
Status: 200
Date: Mon, 01 Jul 2013 17:27:06 GMT
Content-Type: application/json; charset=utf-8
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 56
X-RateLimit-Reset: 1372700873
{
"login": "octocat",
"id": 1,
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false,
"name": "monalisa octocat",
"company": "GitHub",
"blog": "https://github.com/blog",
"location": "San Francisco",
"email": "octocat@github.com",
"hireable": false,
"bio": "There once was...",
"public_repos": 2,
"public_gists": 1,
"followers": 20,
"following": 0,
"created_at": "2008-01-14T04:33:35Z",
"updated_at": "2008-01-14T04:33:35Z",
"total_private_repos": 100,
"owned_private_repos": 100,
"private_gists": 81,
"disk_usage": 10000,
"collaborators": 8,
"two_factor_authentication": true,
"plan": {
"name": "Medium",
"space": 400,
"private_repos": 20,
"collaborators": 0
}
}

@ -0,0 +1,3 @@
package httptest
//go:generate togo httptest -package httptest -output httptest_gen.go

@ -0,0 +1,84 @@
package httptest
import (
"io"
"net/http"
"net/http/httptest"
)
// NewServer starts a new mock http.Server using the test data.
func NewServer() *httptest.Server {
return httptest.NewServer(
http.HandlerFunc(router),
)
}
func router(w http.ResponseWriter, r *http.Request) {
for _, route := range routes {
if route.Method == r.Method && route.Path == r.URL.Path {
w.WriteHeader(route.Status)
route.Header.Write(w)
io.WriteString(w, route.Body)
break
}
}
}
var routes = []struct {
Method string
Path string
Body string
Status int
Header http.Header
}{
// GET /user
{
Method: "GET",
Path: "/user",
Status: 200,
Body: "{\n \"login\": \"octocat\",\n \"id\": 1,\n \"avatar_url\": \"https://github.com/images/error/octocat_happy.gif\",\n \"gravatar_id\": \"\",\n \"url\": \"https://api.github.com/users/octocat\",\n \"html_url\": \"https://github.com/octocat\",\n \"followers_url\": \"https://api.github.com/users/octocat/followers\",\n \"following_url\": \"https://api.github.com/users/octocat/following{/other_user}\",\n \"gists_url\": \"https://api.github.com/users/octocat/gists{/gist_id}\",\n \"starred_url\": \"https://api.github.com/users/octocat/starred{/owner}{/repo}\",\n \"subscriptions_url\": \"https://api.github.com/users/octocat/subscriptions\",\n \"organizations_url\": \"https://api.github.com/users/octocat/orgs\",\n \"repos_url\": \"https://api.github.com/users/octocat/repos\",\n \"events_url\": \"https://api.github.com/users/octocat/events{/privacy}\",\n \"received_events_url\": \"https://api.github.com/users/octocat/received_events\",\n \"type\": \"User\",\n \"site_admin\": false,\n \"name\": \"monalisa octocat\",\n \"company\": \"GitHub\",\n \"blog\": \"https://github.com/blog\",\n \"location\": \"San Francisco\",\n \"email\": \"octocat@github.com\",\n \"hireable\": false,\n \"bio\": \"There once was...\",\n \"public_repos\": 2,\n \"public_gists\": 1,\n \"followers\": 20,\n \"following\": 0,\n \"created_at\": \"2008-01-14T04:33:35Z\",\n \"updated_at\": \"2008-01-14T04:33:35Z\"\n}\n",
Header: map[string][]string{
"Content-Type": {
"application/json; charset=utf-8",
},
"Date": {
"Mon, 01 Jul 2013 17:27:06 GMT",
},
"X-Ratelimit-Limit": {
"60",
},
"X-Ratelimit-Remaining": {
"56",
},
"X-Ratelimit-Reset": {
"1372700873",
},
},
},
// PATCH /user
{
Method: "PATCH",
Path: "/user",
Status: 200,
Body: "{\n \"login\": \"octocat\",\n \"id\": 1,\n \"avatar_url\": \"https://github.com/images/error/octocat_happy.gif\",\n \"gravatar_id\": \"\",\n \"url\": \"https://api.github.com/users/octocat\",\n \"html_url\": \"https://github.com/octocat\",\n \"followers_url\": \"https://api.github.com/users/octocat/followers\",\n \"following_url\": \"https://api.github.com/users/octocat/following{/other_user}\",\n \"gists_url\": \"https://api.github.com/users/octocat/gists{/gist_id}\",\n \"starred_url\": \"https://api.github.com/users/octocat/starred{/owner}{/repo}\",\n \"subscriptions_url\": \"https://api.github.com/users/octocat/subscriptions\",\n \"organizations_url\": \"https://api.github.com/users/octocat/orgs\",\n \"repos_url\": \"https://api.github.com/users/octocat/repos\",\n \"events_url\": \"https://api.github.com/users/octocat/events{/privacy}\",\n \"received_events_url\": \"https://api.github.com/users/octocat/received_events\",\n \"type\": \"User\",\n \"site_admin\": false,\n \"name\": \"monalisa octocat\",\n \"company\": \"GitHub\",\n \"blog\": \"https://github.com/blog\",\n \"location\": \"San Francisco\",\n \"email\": \"octocat@github.com\",\n \"hireable\": false,\n \"bio\": \"There once was...\",\n \"public_repos\": 2,\n \"public_gists\": 1,\n \"followers\": 20,\n \"following\": 0,\n \"created_at\": \"2008-01-14T04:33:35Z\",\n \"updated_at\": \"2008-01-14T04:33:35Z\",\n \"total_private_repos\": 100,\n \"owned_private_repos\": 100,\n \"private_gists\": 81,\n \"disk_usage\": 10000,\n \"collaborators\": 8,\n \"two_factor_authentication\": true,\n \"plan\": {\n \"name\": \"Medium\",\n \"space\": 400,\n \"private_repos\": 20,\n \"collaborators\": 0\n }\n}",
Header: map[string][]string{
"Content-Type": {
"application/json; charset=utf-8",
},
"Date": {
"Mon, 01 Jul 2013 17:27:06 GMT",
},
"X-Ratelimit-Limit": {
"60",
},
"X-Ratelimit-Remaining": {
"56",
},
"X-Ratelimit-Reset": {
"1372700873",
},
},
},
}

@ -0,0 +1,65 @@
package {{ .Package }}
import (
"io"
"net/http"
"net/http/httptest"
)
// NewServer starts a new mock http.Server using the test data.
func NewServer() *httptest.Server {
return httptest.NewServer(
http.HandlerFunc(router),
)
}
func router(w http.ResponseWriter, r *http.Request) {
for _, route := range routes {
if route.Method == r.Method && route.Path == r.URL.Path {
w.WriteHeader(route.Status)
route.Header.Write(w)
io.WriteString(w, route.Body)
break
}
}
}
var routes = []struct {
Method string
Path string
Body string
Status int
Header http.Header
}{
{{ range $index, $route := .Routes }}
// {{ $route.Method }} {{ $route.Path }}
{
Method: {{ printf "%q" $route.Method }},
Path: {{ printf "%q" $route.Path }},
Status: {{ $route.Status }},
{{ if $route.Body -}}
Body: {{ printf "%q" $route.Body }},
{{ end -}}
Header: map[string][]string{
{{ range $key, $value := $route.Header -}}
{{ printf "%q" $key }} : []string{
{{ printf "%q" $value }},
},
{{ end }}
},
},
{{ end }}
}
{{/*
//
// http responses
//
{{ range $index, $route := .Routes }}
{{ if $route.Body -}}
// {{ $route.Method }} {{ $route.Path }}
var response{{ $index }} = `{{ $route.Body }}`
{{ end }}
{{ end }}
*/}}

@ -13,6 +13,9 @@ var files = []struct {
}, {
name: "http.tmpl",
data: string(http),
}, {
name: "httptest.tmpl",
data: string(httptest),
}, {
name: "i18n.tmpl",
data: string(i18n),
@ -489,6 +492,116 @@ var http = []byte{
0x7d, 0x0a,
}
// files/httptest.tmpl
var httptest = []byte{
0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x7b, 0x20, 0x2e,
0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x20, 0x7d, 0x7d, 0x0a, 0x0a,
0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x28, 0x0a, 0x09, 0x22, 0x69,
0x6f, 0x22, 0x0a, 0x09, 0x22, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x74, 0x74,
0x70, 0x22, 0x0a, 0x09, 0x22, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x74, 0x74,
0x70, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x74, 0x65, 0x73, 0x74, 0x22, 0x0a,
0x29, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x4e, 0x65, 0x77, 0x53, 0x65, 0x72,
0x76, 0x65, 0x72, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x20, 0x61,
0x20, 0x6e, 0x65, 0x77, 0x20, 0x6d, 0x6f, 0x63, 0x6b, 0x20, 0x68, 0x74,
0x74, 0x70, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x75, 0x73,
0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x65, 0x73, 0x74,
0x20, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x20,
0x4e, 0x65, 0x77, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x28, 0x29, 0x20,
0x2a, 0x68, 0x74, 0x74, 0x70, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, 0x65,
0x72, 0x76, 0x65, 0x72, 0x20, 0x7b, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75,
0x72, 0x6e, 0x20, 0x68, 0x74, 0x74, 0x70, 0x74, 0x65, 0x73, 0x74, 0x2e,
0x4e, 0x65, 0x77, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x28, 0x0a, 0x09,
0x09, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65,
0x72, 0x46, 0x75, 0x6e, 0x63, 0x28, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72,
0x29, 0x2c, 0x0a, 0x09, 0x29, 0x0a, 0x7d, 0x0a, 0x0a, 0x66, 0x75, 0x6e,
0x63, 0x20, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x28, 0x77, 0x20, 0x68,
0x74, 0x74, 0x70, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x57, 0x72, 0x69, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x72, 0x20, 0x2a, 0x68,
0x74, 0x74, 0x70, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x29,
0x20, 0x7b, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x5f, 0x2c, 0x20, 0x72,
0x6f, 0x75, 0x74, 0x65, 0x20, 0x3a, 0x3d, 0x20, 0x72, 0x61, 0x6e, 0x67,
0x65, 0x20, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x20, 0x7b, 0x0a, 0x09,
0x09, 0x69, 0x66, 0x20, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x2e, 0x4d, 0x65,
0x74, 0x68, 0x6f, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x72, 0x2e, 0x4d, 0x65,
0x74, 0x68, 0x6f, 0x64, 0x20, 0x26, 0x26, 0x20, 0x72, 0x6f, 0x75, 0x74,
0x65, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x20, 0x3d, 0x3d, 0x20, 0x72, 0x2e,
0x55, 0x52, 0x4c, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x20, 0x7b, 0x0a, 0x09,
0x09, 0x09, 0x77, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x48, 0x65, 0x61,
0x64, 0x65, 0x72, 0x28, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x2e, 0x53, 0x74,
0x61, 0x74, 0x75, 0x73, 0x29, 0x0a, 0x09, 0x09, 0x09, 0x72, 0x6f, 0x75,
0x74, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2e, 0x57, 0x72,
0x69, 0x74, 0x65, 0x28, 0x77, 0x29, 0x0a, 0x09, 0x09, 0x09, 0x69, 0x6f,
0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x28, 0x77, 0x2c, 0x20, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x2e, 0x42, 0x6f,
0x64, 0x79, 0x29, 0x0a, 0x09, 0x09, 0x09, 0x62, 0x72, 0x65, 0x61, 0x6b,
0x0a, 0x09, 0x09, 0x7d, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76,
0x61, 0x72, 0x20, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x20, 0x3d, 0x20,
0x5b, 0x5d, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x7b, 0x0a, 0x09,
0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e,
0x67, 0x0a, 0x09, 0x50, 0x61, 0x74, 0x68, 0x20, 0x20, 0x20, 0x73, 0x74,
0x72, 0x69, 0x6e, 0x67, 0x0a, 0x09, 0x42, 0x6f, 0x64, 0x79, 0x20, 0x20,
0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x0a, 0x09, 0x53, 0x74, 0x61,
0x74, 0x75, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x0a, 0x09, 0x48, 0x65, 0x61,
0x64, 0x65, 0x72, 0x20, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x48, 0x65, 0x61,
0x64, 0x65, 0x72, 0x0a, 0x7d, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b,
0x7b, 0x20, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x24, 0x69, 0x6e, 0x64,
0x65, 0x78, 0x2c, 0x20, 0x24, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x20, 0x3a,
0x3d, 0x20, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x20, 0x7d, 0x7d,
0x0a, 0x09, 0x2f, 0x2f, 0x20, 0x7b, 0x7b, 0x20, 0x24, 0x72, 0x6f, 0x75,
0x74, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x7d, 0x7d,
0x20, 0x7b, 0x7b, 0x20, 0x24, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x2e, 0x50,
0x61, 0x74, 0x68, 0x20, 0x7d, 0x7d, 0x0a, 0x09, 0x7b, 0x0a, 0x09, 0x09,
0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x3a, 0x20, 0x7b, 0x7b, 0x20, 0x70,
0x72, 0x69, 0x6e, 0x74, 0x66, 0x20, 0x22, 0x25, 0x71, 0x22, 0x20, 0x24,
0x72, 0x6f, 0x75, 0x74, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64,
0x20, 0x7d, 0x7d, 0x2c, 0x0a, 0x09, 0x09, 0x50, 0x61, 0x74, 0x68, 0x3a,
0x20, 0x20, 0x20, 0x7b, 0x7b, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66,
0x20, 0x22, 0x25, 0x71, 0x22, 0x20, 0x24, 0x72, 0x6f, 0x75, 0x74, 0x65,
0x2e, 0x50, 0x61, 0x74, 0x68, 0x20, 0x7d, 0x7d, 0x2c, 0x0a, 0x09, 0x09,
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x20, 0x7b, 0x7b, 0x20, 0x24,
0x72, 0x6f, 0x75, 0x74, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
0x20, 0x7d, 0x7d, 0x2c, 0x0a, 0x09, 0x09, 0x7b, 0x7b, 0x20, 0x69, 0x66,
0x20, 0x24, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x2e, 0x42, 0x6f, 0x64, 0x79,
0x20, 0x2d, 0x7d, 0x7d, 0x0a, 0x09, 0x09, 0x42, 0x6f, 0x64, 0x79, 0x3a,
0x20, 0x20, 0x20, 0x7b, 0x7b, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66,
0x20, 0x22, 0x25, 0x71, 0x22, 0x20, 0x24, 0x72, 0x6f, 0x75, 0x74, 0x65,
0x2e, 0x42, 0x6f, 0x64, 0x79, 0x20, 0x7d, 0x7d, 0x2c, 0x0a, 0x09, 0x09,
0x7b, 0x7b, 0x20, 0x65, 0x6e, 0x64, 0x20, 0x2d, 0x7d, 0x7d, 0x0a, 0x09,
0x09, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x6d, 0x61, 0x70,
0x5b, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5d, 0x5b, 0x5d, 0x73, 0x74,
0x72, 0x69, 0x6e, 0x67, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x7b, 0x7b, 0x20,
0x72, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x24, 0x6b, 0x65, 0x79, 0x2c, 0x20,
0x24, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x3a, 0x3d, 0x20, 0x24, 0x72,
0x6f, 0x75, 0x74, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x20,
0x2d, 0x7d, 0x7d, 0x0a, 0x09, 0x09, 0x09, 0x7b, 0x7b, 0x20, 0x70, 0x72,
0x69, 0x6e, 0x74, 0x66, 0x20, 0x22, 0x25, 0x71, 0x22, 0x20, 0x24, 0x6b,
0x65, 0x79, 0x20, 0x7d, 0x7d, 0x20, 0x3a, 0x20, 0x5b, 0x5d, 0x73, 0x74,
0x72, 0x69, 0x6e, 0x67, 0x7b, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x7b, 0x7b,
0x20, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x20, 0x22, 0x25, 0x71, 0x22,
0x20, 0x24, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x7d, 0x7d, 0x2c, 0x0a,
0x09, 0x09, 0x09, 0x7d, 0x2c, 0x0a, 0x09, 0x09, 0x09, 0x7b, 0x7b, 0x20,
0x65, 0x6e, 0x64, 0x20, 0x7d, 0x7d, 0x0a, 0x09, 0x09, 0x7d, 0x2c, 0x0a,
0x09, 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x7b, 0x20, 0x65,
0x6e, 0x64, 0x20, 0x7d, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x7b, 0x7b, 0x2f,
0x2a, 0x0a, 0x2f, 0x2f, 0x0a, 0x2f, 0x2f, 0x20, 0x68, 0x74, 0x74, 0x70,
0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x0a, 0x2f,
0x2f, 0x0a, 0x0a, 0x7b, 0x7b, 0x20, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x20,
0x24, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x24, 0x72, 0x6f, 0x75,
0x74, 0x65, 0x20, 0x3a, 0x3d, 0x20, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65,
0x73, 0x20, 0x7d, 0x7d, 0x0a, 0x7b, 0x7b, 0x20, 0x69, 0x66, 0x20, 0x24,
0x72, 0x6f, 0x75, 0x74, 0x65, 0x2e, 0x42, 0x6f, 0x64, 0x79, 0x20, 0x2d,
0x7d, 0x7d, 0x0a, 0x2f, 0x2f, 0x20, 0x7b, 0x7b, 0x20, 0x24, 0x72, 0x6f,
0x75, 0x74, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x7d,
0x7d, 0x20, 0x7b, 0x7b, 0x20, 0x24, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x2e,
0x50, 0x61, 0x74, 0x68, 0x20, 0x7d, 0x7d, 0x0a, 0x76, 0x61, 0x72, 0x20,
0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x7b, 0x7b, 0x20, 0x24,
0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x7d, 0x7d, 0x20, 0x3d, 0x20, 0x60,
0x7b, 0x7b, 0x20, 0x24, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x2e, 0x42, 0x6f,
0x64, 0x79, 0x20, 0x7d, 0x7d, 0x60, 0x0a, 0x7b, 0x7b, 0x20, 0x65, 0x6e,
0x64, 0x20, 0x7d, 0x7d, 0x0a, 0x7b, 0x7b, 0x20, 0x65, 0x6e, 0x64, 0x20,
0x7d, 0x7d, 0x0a, 0x2a, 0x2f, 0x7d, 0x7d, 0x0a,
}
// files/i18n.tmpl
var i18n = []byte{
0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x7b, 0x20, 0x2e,

Loading…
Cancel
Save