From f2079bcb02185b97591543e3cc583cf02cb303b2 Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Sat, 24 Aug 2024 10:54:33 -0700
Subject: [PATCH 01/26] remove unused variable
---
generate.go | 1 -
1 file changed, 1 deletion(-)
diff --git a/generate.go b/generate.go
index b304d48..d5eed59 100644
--- a/generate.go
+++ b/generate.go
@@ -21,7 +21,6 @@ const (
muxVarIdent = "mux"
requestPathValue = "PathValue"
- templatesLookup = "Lookup"
httpRequestContextMethod = "Context"
httpPackageIdent = "http"
httpResponseWriterIdent = "ResponseWriter"
From 9672cc5210dd8f53f28ee9b9c5cca235f36707d6 Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Mon, 26 Aug 2024 10:45:01 -0700
Subject: [PATCH 02/26] add template param to Generate
---
cmd/muxt/generate.go | 2 +-
generate.go | 3 ++-
generate_test.go | 2 +-
3 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/cmd/muxt/generate.go b/cmd/muxt/generate.go
index 4b8c2e9..e56d2a1 100644
--- a/cmd/muxt/generate.go
+++ b/cmd/muxt/generate.go
@@ -100,7 +100,7 @@ func generateCommand(args []string, workingDirectory string, getEnv func(string)
return err
}
out := log.New(stdout, "", 0)
- s, err := muxt.Generate(templateNames, g.goPackage, g.templatesVariable, g.routesFunction, g.receiverIdent, g.Package.Fset, g.Package.Syntax, g.Package.Syntax, out)
+ s, err := muxt.Generate(templateNames, ts, g.goPackage, g.templatesVariable, g.routesFunction, g.receiverIdent, g.Package.Fset, g.Package.Syntax, g.Package.Syntax, out)
if err != nil {
return err
}
diff --git a/generate.go b/generate.go
index d5eed59..5ce7b0f 100644
--- a/generate.go
+++ b/generate.go
@@ -5,6 +5,7 @@ import (
"fmt"
"go/ast"
"go/token"
+ "html/template"
"log"
"slices"
"strconv"
@@ -41,7 +42,7 @@ const (
receiverInterfaceIdent = "RoutesReceiver"
)
-func Generate(templateNames []TemplateName, packageName, templatesVariableName, routesFunctionName, receiverTypeIdent string, _ *token.FileSet, receiverPackage, templatesPackage []*ast.File, log *log.Logger) (string, error) {
+func Generate(templateNames []TemplateName, _ *template.Template, packageName, templatesVariableName, routesFunctionName, receiverTypeIdent string, _ *token.FileSet, receiverPackage, templatesPackage []*ast.File, log *log.Logger) (string, error) {
packageName = cmp.Or(packageName, defaultPackageName)
templatesVariableName = cmp.Or(templatesVariableName, DefaultTemplatesVariableName)
routesFunctionName = cmp.Or(routesFunctionName, DefaultRoutesFunctionName)
diff --git a/generate_test.go b/generate_test.go
index d34e5e3..8d9679d 100644
--- a/generate_test.go
+++ b/generate_test.go
@@ -571,7 +571,7 @@ func execute(response http.ResponseWriter, request *http.Request, writeHeader bo
logs := log.New(io.Discard, "", 0)
set := token.NewFileSet()
goFiles := methodFuncTypeLoader(t, set, tt.ReceiverPackage)
- out, err := muxt.Generate(templateNames, tt.PackageName, tt.TemplatesVar, tt.RoutesFunc, tt.Receiver, set, goFiles, goFiles, logs)
+ out, err := muxt.Generate(templateNames, ts, tt.PackageName, tt.TemplatesVar, tt.RoutesFunc, tt.Receiver, set, goFiles, goFiles, logs)
if tt.ExpectedError == "" {
assert.NoError(t, err)
assert.Equal(t, tt.ExpectedFile, out)
From 7547b18f8fcaef2b048703e48bf54407d9488332 Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Mon, 26 Aug 2024 10:45:27 -0700
Subject: [PATCH 03/26] refactor: rename pattern variable to name
---
generate.go | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/generate.go b/generate.go
index 5ce7b0f..9172c1b 100644
--- a/generate.go
+++ b/generate.go
@@ -60,33 +60,33 @@ func Generate(templateNames []TemplateName, _ *template.Template, packageName, t
imports := []*ast.ImportSpec{
importSpec("net/" + httpPackageIdent),
}
- for _, pattern := range templateNames {
+ for _, name := range templateNames {
var method *ast.FuncType
- if pattern.fun != nil {
+ if name.fun != nil {
for _, funcDecl := range source.IterateFunctions(receiverPackage) {
- if !pattern.matchReceiver(funcDecl, receiverTypeIdent) {
+ if !name.matchReceiver(funcDecl, receiverTypeIdent) {
continue
}
method = funcDecl.Type
break
}
if method == nil {
- me, im := pattern.funcType()
+ me, im := name.funcType()
method = me
imports = append(imports, im...)
}
receiverInterface.Methods.List = append(receiverInterface.Methods.List, &ast.Field{
- Names: []*ast.Ident{ast.NewIdent(pattern.fun.Name)},
+ Names: []*ast.Ident{ast.NewIdent(name.fun.Name)},
Type: method,
})
}
- handlerFunc, methodImports, err := pattern.funcLit(method)
+ handlerFunc, methodImports, err := name.funcLit(method)
if err != nil {
return "", err
}
imports = sortImports(append(imports, methodImports...))
- routes.Body.List = append(routes.Body.List, pattern.callHandleFunc(handlerFunc))
- log.Printf("%s has route for %s", routesFunctionName, pattern.String())
+ routes.Body.List = append(routes.Body.List, name.callHandleFunc(handlerFunc))
+ log.Printf("%s has route for %s", routesFunctionName, name.String())
}
importGen := &ast.GenDecl{
Tok: token.IMPORT,
From 12519f927fa1f4f9edb5525e85e7726c8177d0ea Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Mon, 26 Aug 2024 14:13:56 -0700
Subject: [PATCH 04/26] rename ast -> go
---
internal/source/{ast.go => go.go} | 0
internal/source/{ast_test.go => go_test.go} | 0
2 files changed, 0 insertions(+), 0 deletions(-)
rename internal/source/{ast.go => go.go} (100%)
rename internal/source/{ast_test.go => go_test.go} (100%)
diff --git a/internal/source/ast.go b/internal/source/go.go
similarity index 100%
rename from internal/source/ast.go
rename to internal/source/go.go
diff --git a/internal/source/ast_test.go b/internal/source/go_test.go
similarity index 100%
rename from internal/source/ast_test.go
rename to internal/source/go_test.go
From 204380d223bd699fa51ede6f73075dd301a62620 Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Mon, 26 Aug 2024 18:09:09 -0700
Subject: [PATCH 05/26] refactor: add helper function for generating status
code expressions
---
generate.go | 21 ++++-------
internal/source/go.go | 81 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 87 insertions(+), 15 deletions(-)
diff --git a/generate.go b/generate.go
index 9172c1b..83ad09d 100644
--- a/generate.go
+++ b/generate.go
@@ -7,6 +7,7 @@ import (
"go/token"
"html/template"
"log"
+ "net/http"
"slices"
"strconv"
"strings"
@@ -27,9 +28,6 @@ const (
httpResponseWriterIdent = "ResponseWriter"
httpServeMuxIdent = "ServeMux"
httpRequestIdent = "Request"
- httpStatusCode200Ident = "StatusOK"
- httpStatusCode500Ident = "StatusInternalServerError"
- httpStatusCode400Ident = "StatusBadRequest"
httpHandleFuncIdent = "HandleFunc"
contextPackageIdent = "context"
@@ -200,7 +198,7 @@ func (def TemplateName) funcLit(method *ast.FuncType) (*ast.FuncLit, []*ast.Impo
},
Args: []ast.Expr{},
},
- httpStatusCode(httpStatusCode500Ident),
+ source.HTTPStatusCode(httpPackageIdent, http.StatusInternalServerError),
},
}},
&ast.ReturnStmt{},
@@ -211,7 +209,7 @@ func (def TemplateName) funcLit(method *ast.FuncType) (*ast.FuncLit, []*ast.Impo
} else {
lit.Body.List = append(lit.Body.List, &ast.AssignStmt{Lhs: []ast.Expr{ast.NewIdent(dataVarIdent)}, Tok: token.DEFINE, Rhs: []ast.Expr{call}})
}
- lit.Body.List = append(lit.Body.List, def.executeCall(httpStatusCode(httpStatusCode200Ident), ast.NewIdent(dataVarIdent), writeHeader))
+ lit.Body.List = append(lit.Body.List, def.executeCall(source.HTTPStatusCode(httpPackageIdent, http.StatusOK), ast.NewIdent(dataVarIdent), writeHeader))
return lit, imports, nil
}
@@ -371,13 +369,6 @@ func contextContextType() *ast.SelectorExpr {
return &ast.SelectorExpr{X: ast.NewIdent(contextPackageIdent), Sel: ast.NewIdent(contextContextTypeIdent)}
}
-func httpStatusCode(name string) *ast.SelectorExpr {
- return &ast.SelectorExpr{
- X: ast.NewIdent(httpPackageIdent),
- Sel: ast.NewIdent(name),
- }
-}
-
func contextAssignment() *ast.AssignStmt {
return &ast.AssignStmt{
Tok: token.DEFINE,
@@ -827,7 +818,7 @@ func paramParseError(errVar *ast.Ident) *ast.IfStmt {
},
Args: []ast.Expr{},
},
- httpStatusCode(httpStatusCode400Ident),
+ source.HTTPStatusCode(httpPackageIdent, http.StatusBadRequest),
},
}},
&ast.ReturnStmt{},
@@ -853,7 +844,7 @@ func (def TemplateName) executeCall(status, data ast.Expr, writeHeader bool) *as
func (def TemplateName) httpRequestReceiverTemplateHandlerFunc() *ast.FuncLit {
return &ast.FuncLit{
Type: httpHandlerFuncType(),
- Body: &ast.BlockStmt{List: []ast.Stmt{def.executeCall(httpStatusCode(httpStatusCode200Ident), ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest), true)}},
+ Body: &ast.BlockStmt{List: []ast.Stmt{def.executeCall(source.HTTPStatusCode(httpPackageIdent, http.StatusOK), ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest), true)}},
}
}
@@ -932,7 +923,7 @@ func executeFuncDecl(templatesVariableIdent string) *ast.FuncDecl {
},
Args: []ast.Expr{},
},
- httpStatusCode(httpStatusCode500Ident),
+ source.HTTPStatusCode(httpPackageIdent, http.StatusInternalServerError),
},
}},
&ast.ReturnStmt{},
diff --git a/internal/source/go.go b/internal/source/go.go
index 133f266..2326ed3 100644
--- a/internal/source/go.go
+++ b/internal/source/go.go
@@ -5,6 +5,7 @@ import (
"go/ast"
"go/printer"
"go/token"
+ "net/http"
"strconv"
"strings"
)
@@ -141,3 +142,83 @@ func IterateFieldTypes(list []*ast.Field) func(func(int, ast.Expr) bool) {
}
}
}
+
+var httpCodes = map[int]string{
+ http.StatusContinue: "StatusContinue",
+ http.StatusSwitchingProtocols: "StatusSwitchingProtocols",
+ http.StatusProcessing: "StatusProcessing",
+ http.StatusEarlyHints: "StatusEarlyHints",
+
+ http.StatusOK: "StatusOK",
+ http.StatusCreated: "StatusCreated",
+ http.StatusAccepted: "StatusAccepted",
+ http.StatusNonAuthoritativeInfo: "StatusNonAuthoritativeInfo",
+ http.StatusNoContent: "StatusNoContent",
+ http.StatusResetContent: "StatusResetContent",
+ http.StatusPartialContent: "StatusPartialContent",
+ http.StatusMultiStatus: "StatusMultiStatus",
+ http.StatusAlreadyReported: "StatusAlreadyReported",
+ http.StatusIMUsed: "StatusIMUsed",
+
+ http.StatusMultipleChoices: "StatusMultipleChoices",
+ http.StatusMovedPermanently: "StatusMovedPermanently",
+ http.StatusFound: "StatusFound",
+ http.StatusSeeOther: "StatusSeeOther",
+ http.StatusNotModified: "StatusNotModified",
+ http.StatusUseProxy: "StatusUseProxy",
+ http.StatusTemporaryRedirect: "StatusTemporaryRedirect",
+ http.StatusPermanentRedirect: "StatusPermanentRedirect",
+
+ http.StatusBadRequest: "StatusBadRequest",
+ http.StatusUnauthorized: "StatusUnauthorized",
+ http.StatusPaymentRequired: "StatusPaymentRequired",
+ http.StatusForbidden: "StatusForbidden",
+ http.StatusNotFound: "StatusNotFound",
+ http.StatusMethodNotAllowed: "StatusMethodNotAllowed",
+ http.StatusNotAcceptable: "StatusNotAcceptable",
+ http.StatusProxyAuthRequired: "StatusProxyAuthRequired",
+ http.StatusRequestTimeout: "StatusRequestTimeout",
+ http.StatusConflict: "StatusConflict",
+ http.StatusGone: "StatusGone",
+ http.StatusLengthRequired: "StatusLengthRequired",
+ http.StatusPreconditionFailed: "StatusPreconditionFailed",
+ http.StatusRequestEntityTooLarge: "StatusRequestEntityTooLarge",
+ http.StatusRequestURITooLong: "StatusRequestURITooLong",
+ http.StatusUnsupportedMediaType: "StatusUnsupportedMediaType",
+ http.StatusRequestedRangeNotSatisfiable: "StatusRequestedRangeNotSatisfiable",
+ http.StatusExpectationFailed: "StatusExpectationFailed",
+ http.StatusTeapot: "StatusTeapot",
+ http.StatusMisdirectedRequest: "StatusMisdirectedRequest",
+ http.StatusUnprocessableEntity: "StatusUnprocessableEntity",
+ http.StatusLocked: "StatusLocked",
+ http.StatusFailedDependency: "StatusFailedDependency",
+ http.StatusTooEarly: "StatusTooEarly",
+ http.StatusUpgradeRequired: "StatusUpgradeRequired",
+ http.StatusPreconditionRequired: "StatusPreconditionRequired",
+ http.StatusTooManyRequests: "StatusTooManyRequests",
+ http.StatusRequestHeaderFieldsTooLarge: "StatusRequestHeaderFieldsTooLarge",
+ http.StatusUnavailableForLegalReasons: "StatusUnavailableForLegalReasons",
+
+ http.StatusInternalServerError: "StatusInternalServerError",
+ http.StatusNotImplemented: "StatusNotImplemented",
+ http.StatusBadGateway: "StatusBadGateway",
+ http.StatusServiceUnavailable: "StatusServiceUnavailable",
+ http.StatusGatewayTimeout: "StatusGatewayTimeout",
+ http.StatusHTTPVersionNotSupported: "StatusHTTPVersionNotSupported",
+ http.StatusVariantAlsoNegotiates: "StatusVariantAlsoNegotiates",
+ http.StatusInsufficientStorage: "StatusInsufficientStorage",
+ http.StatusLoopDetected: "StatusLoopDetected",
+ http.StatusNotExtended: "StatusNotExtended",
+ http.StatusNetworkAuthenticationRequired: "StatusNetworkAuthenticationRequired",
+}
+
+func HTTPStatusCode(pkg string, n int) ast.Expr {
+ ident, ok := httpCodes[n]
+ if !ok {
+ return &ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(n)}
+ }
+ return &ast.SelectorExpr{
+ X: ast.NewIdent(pkg),
+ Sel: ast.NewIdent(ident),
+ }
+}
From 25837999054844dfffe4277928ab8f34354ddd11 Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Mon, 26 Aug 2024 18:16:36 -0700
Subject: [PATCH 06/26] remove unused template name scope
---
name.go | 4 ----
1 file changed, 4 deletions(-)
diff --git a/name.go b/name.go
index 3437d38..90bfbe0 100644
--- a/name.go
+++ b/name.go
@@ -173,8 +173,6 @@ const (
TemplateNameScopeIdentifierHTTPRequest = "request"
TemplateNameScopeIdentifierHTTPResponse = "response"
TemplateNameScopeIdentifierContext = "ctx"
- TemplateNameScopeIdentifierTemplate = "template"
- TemplateNameScopeIdentifierLogger = "logger"
)
func patternScope() []string {
@@ -182,7 +180,5 @@ func patternScope() []string {
TemplateNameScopeIdentifierHTTPRequest,
TemplateNameScopeIdentifierHTTPResponse,
TemplateNameScopeIdentifierContext,
- TemplateNameScopeIdentifierTemplate,
- TemplateNameScopeIdentifierLogger,
}
}
From e78bdc9f62ee77fca3225bc3975fb171be17dba2 Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Mon, 26 Aug 2024 18:21:18 -0700
Subject: [PATCH 07/26] refactor: factor our errVar
---
generate.go | 27 +++------------------------
1 file changed, 3 insertions(+), 24 deletions(-)
diff --git a/generate.go b/generate.go
index 83ad09d..2300313 100644
--- a/generate.go
+++ b/generate.go
@@ -164,7 +164,8 @@ func (def TemplateName) funcLit(method *ast.FuncType) (*ast.FuncLit, []*ast.Impo
call.Args = append(call.Args, ast.NewIdent(TemplateNameScopeIdentifierContext))
imports = append(imports, importSpec("context"))
default:
- statements, parseImports, err := httpPathValueAssignment(method, i, arg)
+ errVar := ast.NewIdent("err")
+ statements, parseImports, err := httpPathValueAssignment(method, i, arg, errVar)
if err != nil {
return nil, nil, err
}
@@ -382,7 +383,7 @@ func contextAssignment() *ast.AssignStmt {
}
}
-func httpPathValueAssignment(method *ast.FuncType, i int, arg *ast.Ident) ([]ast.Stmt, []*ast.ImportSpec, error) {
+func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident) ([]ast.Stmt, []*ast.ImportSpec, error) {
const parsedVarSuffix = "Parsed"
for typeIndex, typeExp := range source.IterateFieldTypes(method.Params.List) {
if typeIndex != i {
@@ -396,8 +397,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg *ast.Ident) ([]ast
default:
return nil, nil, fmt.Errorf("method param type %s not supported", source.Format(typeExp))
case "bool":
- errVar := ast.NewIdent("err")
-
assign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(arg.Name), ast.NewIdent(errVar.Name)},
Tok: token.DEFINE,
@@ -420,8 +419,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg *ast.Ident) ([]ast
return []ast.Stmt{assign, errCheck}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "int":
- errVar := ast.NewIdent("err")
-
tmp := arg.Name + parsedVarSuffix
parse := &ast.AssignStmt{
@@ -458,8 +455,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg *ast.Ident) ([]ast
return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "int16":
- errVar := ast.NewIdent("err")
-
tmp := arg.Name + parsedVarSuffix
parse := &ast.AssignStmt{
@@ -496,8 +491,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg *ast.Ident) ([]ast
return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "int32":
- errVar := ast.NewIdent("err")
-
tmp := arg.Name + parsedVarSuffix
parse := &ast.AssignStmt{
@@ -534,8 +527,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg *ast.Ident) ([]ast
return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "int8":
- errVar := ast.NewIdent("err")
-
tmp := arg.Name + parsedVarSuffix
parse := &ast.AssignStmt{
@@ -572,8 +563,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg *ast.Ident) ([]ast
return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "int64":
- errVar := ast.NewIdent("err")
-
assign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(arg.Name), ast.NewIdent(errVar.Name)},
Tok: token.DEFINE,
@@ -600,8 +589,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg *ast.Ident) ([]ast
return []ast.Stmt{assign, errCheck}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "uint":
- errVar := ast.NewIdent("err")
-
tmp := arg.Name + parsedVarSuffix
parse := &ast.AssignStmt{
@@ -638,8 +625,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg *ast.Ident) ([]ast
return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "uint16":
- errVar := ast.NewIdent("err")
-
tmp := arg.Name + parsedVarSuffix
parse := &ast.AssignStmt{
@@ -676,8 +661,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg *ast.Ident) ([]ast
return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "uint32":
- errVar := ast.NewIdent("err")
-
tmp := arg.Name + parsedVarSuffix
parse := &ast.AssignStmt{
@@ -715,8 +698,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg *ast.Ident) ([]ast
return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "uint64":
- errVar := ast.NewIdent("err")
-
assign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(arg.Name), ast.NewIdent(errVar.Name)},
Tok: token.DEFINE,
@@ -743,8 +724,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg *ast.Ident) ([]ast
return []ast.Stmt{assign, errCheck}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "uint8":
- errVar := ast.NewIdent("err")
-
tmp := arg.Name + parsedVarSuffix
parse := &ast.AssignStmt{
From aead362144e61bf443b002990d0ee48ed84beca8 Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Mon, 26 Aug 2024 18:27:46 -0700
Subject: [PATCH 08/26] refactor: move err checkout of assignment func
---
generate.go | 19 +++----------------
1 file changed, 3 insertions(+), 16 deletions(-)
diff --git a/generate.go b/generate.go
index 2300313..c5d3a91 100644
--- a/generate.go
+++ b/generate.go
@@ -165,7 +165,8 @@ func (def TemplateName) funcLit(method *ast.FuncType) (*ast.FuncLit, []*ast.Impo
imports = append(imports, importSpec("context"))
default:
errVar := ast.NewIdent("err")
- statements, parseImports, err := httpPathValueAssignment(method, i, arg, errVar)
+ errCheck := paramParseError(errVar)
+ statements, parseImports, err := httpPathValueAssignment(method, i, arg, errVar, errCheck)
if err != nil {
return nil, nil, err
}
@@ -383,7 +384,7 @@ func contextAssignment() *ast.AssignStmt {
}
}
-func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident) ([]ast.Stmt, []*ast.ImportSpec, error) {
+func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident, errCheck *ast.IfStmt) ([]ast.Stmt, []*ast.ImportSpec, error) {
const parsedVarSuffix = "Parsed"
for typeIndex, typeExp := range source.IterateFieldTypes(method.Params.List) {
if typeIndex != i {
@@ -415,8 +416,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
}},
}
- errCheck := paramParseError(errVar)
-
return []ast.Stmt{assign, errCheck}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "int":
tmp := arg.Name + parsedVarSuffix
@@ -443,7 +442,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
}},
}
- errCheck := paramParseError(errVar)
assign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
Tok: token.DEFINE,
@@ -479,7 +477,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
}},
}
- errCheck := paramParseError(errVar)
assign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
Tok: token.DEFINE,
@@ -515,7 +512,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
}},
}
- errCheck := paramParseError(errVar)
assign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
Tok: token.DEFINE,
@@ -551,7 +547,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
}},
}
- errCheck := paramParseError(errVar)
assign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
Tok: token.DEFINE,
@@ -585,8 +580,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
}},
}
- errCheck := paramParseError(errVar)
-
return []ast.Stmt{assign, errCheck}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "uint":
tmp := arg.Name + parsedVarSuffix
@@ -613,7 +606,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
}},
}
- errCheck := paramParseError(errVar)
assign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
Tok: token.DEFINE,
@@ -649,7 +641,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
}},
}
- errCheck := paramParseError(errVar)
assign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
Tok: token.DEFINE,
@@ -685,7 +676,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
}},
}
- errCheck := paramParseError(errVar)
assign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
Tok: token.DEFINE,
@@ -720,8 +710,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
}},
}
- errCheck := paramParseError(errVar)
-
return []ast.Stmt{assign, errCheck}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "uint8":
tmp := arg.Name + parsedVarSuffix
@@ -748,7 +736,6 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
}},
}
- errCheck := paramParseError(errVar)
assign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
Tok: token.DEFINE,
From 63695a5d92f6b8ef6e0aa4dc5b7dc3a739e0d82b Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Mon, 26 Aug 2024 18:31:10 -0700
Subject: [PATCH 09/26] refactor: pass in str source
---
generate.go | 107 ++++++++++------------------------------------------
1 file changed, 20 insertions(+), 87 deletions(-)
diff --git a/generate.go b/generate.go
index c5d3a91..fd40645 100644
--- a/generate.go
+++ b/generate.go
@@ -166,7 +166,14 @@ func (def TemplateName) funcLit(method *ast.FuncType) (*ast.FuncLit, []*ast.Impo
default:
errVar := ast.NewIdent("err")
errCheck := paramParseError(errVar)
- statements, parseImports, err := httpPathValueAssignment(method, i, arg, errVar, errCheck)
+ src := &ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
+ Sel: ast.NewIdent(requestPathValue),
+ },
+ Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(arg.Name)}},
+ }
+ statements, parseImports, err := httpPathValueAssignment(method, i, arg, errVar, src, errCheck)
if err != nil {
return nil, nil, err
}
@@ -384,7 +391,7 @@ func contextAssignment() *ast.AssignStmt {
}
}
-func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident, errCheck *ast.IfStmt) ([]ast.Stmt, []*ast.ImportSpec, error) {
+func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident, str ast.Expr, errCheck *ast.IfStmt) ([]ast.Stmt, []*ast.ImportSpec, error) {
const parsedVarSuffix = "Parsed"
for typeIndex, typeExp := range source.IterateFieldTypes(method.Params.List) {
if typeIndex != i {
@@ -406,13 +413,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
X: ast.NewIdent("strconv"),
Sel: ast.NewIdent("ParseBool"),
},
- Args: []ast.Expr{&ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
- Sel: ast.NewIdent(requestPathValue),
- },
- Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(arg.Name)}},
- }},
+ Args: []ast.Expr{str},
}},
}
@@ -428,17 +429,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
X: ast.NewIdent("strconv"),
Sel: ast.NewIdent("ParseInt"),
},
- Args: []ast.Expr{
- &ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
- Sel: ast.NewIdent(requestPathValue),
- },
- Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(arg.Name)}},
- },
- &ast.BasicLit{Value: "10", Kind: token.INT},
- &ast.BasicLit{Value: "64", Kind: token.INT},
- },
+ Args: []ast.Expr{str},
}},
}
@@ -463,17 +454,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
X: ast.NewIdent("strconv"),
Sel: ast.NewIdent("ParseInt"),
},
- Args: []ast.Expr{
- &ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
- Sel: ast.NewIdent(requestPathValue),
- },
- Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(arg.Name)}},
- },
- &ast.BasicLit{Value: "10", Kind: token.INT},
- &ast.BasicLit{Value: "16", Kind: token.INT},
- },
+ Args: []ast.Expr{str},
}},
}
@@ -499,13 +480,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
Sel: ast.NewIdent("ParseInt"),
},
Args: []ast.Expr{
- &ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
- Sel: ast.NewIdent(requestPathValue),
- },
- Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(arg.Name)}},
- },
+ str,
&ast.BasicLit{Value: "10", Kind: token.INT},
&ast.BasicLit{Value: "32", Kind: token.INT},
},
@@ -534,13 +509,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
Sel: ast.NewIdent("ParseInt"),
},
Args: []ast.Expr{
- &ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
- Sel: ast.NewIdent(requestPathValue),
- },
- Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(arg.Name)}},
- },
+ str,
&ast.BasicLit{Value: "10", Kind: token.INT},
&ast.BasicLit{Value: "8", Kind: token.INT},
},
@@ -567,13 +536,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
Sel: ast.NewIdent("ParseInt"),
},
Args: []ast.Expr{
- &ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
- Sel: ast.NewIdent(requestPathValue),
- },
- Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(arg.Name)}},
- },
+ str,
&ast.BasicLit{Value: "10", Kind: token.INT},
&ast.BasicLit{Value: "64", Kind: token.INT},
},
@@ -593,13 +556,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
Sel: ast.NewIdent("ParseUint"),
},
Args: []ast.Expr{
- &ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
- Sel: ast.NewIdent(requestPathValue),
- },
- Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(arg.Name)}},
- },
+ str,
&ast.BasicLit{Value: "10", Kind: token.INT},
&ast.BasicLit{Value: "64", Kind: token.INT},
},
@@ -628,13 +585,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
Sel: ast.NewIdent("ParseUint"),
},
Args: []ast.Expr{
- &ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
- Sel: ast.NewIdent(requestPathValue),
- },
- Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(arg.Name)}},
- },
+ str,
&ast.BasicLit{Value: "10", Kind: token.INT},
&ast.BasicLit{Value: "16", Kind: token.INT},
},
@@ -663,13 +614,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
Sel: ast.NewIdent("ParseUint"),
},
Args: []ast.Expr{
- &ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
- Sel: ast.NewIdent(requestPathValue),
- },
- Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(arg.Name)}},
- },
+ str,
&ast.BasicLit{Value: "10", Kind: token.INT},
&ast.BasicLit{Value: "32", Kind: token.INT},
},
@@ -697,13 +642,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
Sel: ast.NewIdent("ParseUint"),
},
Args: []ast.Expr{
- &ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
- Sel: ast.NewIdent(requestPathValue),
- },
- Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(arg.Name)}},
- },
+ str,
&ast.BasicLit{Value: "10", Kind: token.INT},
&ast.BasicLit{Value: "64", Kind: token.INT},
},
@@ -723,13 +662,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
Sel: ast.NewIdent("ParseUint"),
},
Args: []ast.Expr{
- &ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
- Sel: ast.NewIdent(requestPathValue),
- },
- Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(arg.Name)}},
- },
+ str,
&ast.BasicLit{Value: "10", Kind: token.INT},
&ast.BasicLit{Value: "8", Kind: token.INT},
},
From 63f079ef6814e83419fa4c0fbca6d0360695fdbb Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Mon, 26 Aug 2024 18:37:36 -0700
Subject: [PATCH 10/26] refactor: use Atoi for int and factor out lit to source
pkg
---
generate.go | 53 +++++++++----------------------------------
generate_test.go | 2 +-
internal/source/go.go | 2 ++
3 files changed, 14 insertions(+), 43 deletions(-)
diff --git a/generate.go b/generate.go
index fd40645..598e0b1 100644
--- a/generate.go
+++ b/generate.go
@@ -401,6 +401,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
if !ok {
return nil, nil, fmt.Errorf("unsupported type: %s", source.Format(typeExp))
}
+ base10 := source.Int(10)
switch paramTypeIdent.Name {
default:
return nil, nil, fmt.Errorf("method param type %s not supported", source.Format(typeExp))
@@ -427,7 +428,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
Rhs: []ast.Expr{&ast.CallExpr{
Fun: &ast.SelectorExpr{
X: ast.NewIdent("strconv"),
- Sel: ast.NewIdent("ParseInt"),
+ Sel: ast.NewIdent("Atoi"),
},
Args: []ast.Expr{str},
}},
@@ -454,7 +455,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
X: ast.NewIdent("strconv"),
Sel: ast.NewIdent("ParseInt"),
},
- Args: []ast.Expr{str},
+ Args: []ast.Expr{str, base10, source.Int(16)},
}},
}
@@ -479,11 +480,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
X: ast.NewIdent("strconv"),
Sel: ast.NewIdent("ParseInt"),
},
- Args: []ast.Expr{
- str,
- &ast.BasicLit{Value: "10", Kind: token.INT},
- &ast.BasicLit{Value: "32", Kind: token.INT},
- },
+ Args: []ast.Expr{str, base10, source.Int(32)},
}},
}
@@ -508,11 +505,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
X: ast.NewIdent("strconv"),
Sel: ast.NewIdent("ParseInt"),
},
- Args: []ast.Expr{
- str,
- &ast.BasicLit{Value: "10", Kind: token.INT},
- &ast.BasicLit{Value: "8", Kind: token.INT},
- },
+ Args: []ast.Expr{str, base10, source.Int(8)},
}},
}
@@ -535,11 +528,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
X: ast.NewIdent("strconv"),
Sel: ast.NewIdent("ParseInt"),
},
- Args: []ast.Expr{
- str,
- &ast.BasicLit{Value: "10", Kind: token.INT},
- &ast.BasicLit{Value: "64", Kind: token.INT},
- },
+ Args: []ast.Expr{str, base10, source.Int(64)},
}},
}
@@ -555,11 +544,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
X: ast.NewIdent("strconv"),
Sel: ast.NewIdent("ParseUint"),
},
- Args: []ast.Expr{
- str,
- &ast.BasicLit{Value: "10", Kind: token.INT},
- &ast.BasicLit{Value: "64", Kind: token.INT},
- },
+ Args: []ast.Expr{str, base10, source.Int(64)},
}},
}
@@ -584,11 +569,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
X: ast.NewIdent("strconv"),
Sel: ast.NewIdent("ParseUint"),
},
- Args: []ast.Expr{
- str,
- &ast.BasicLit{Value: "10", Kind: token.INT},
- &ast.BasicLit{Value: "16", Kind: token.INT},
- },
+ Args: []ast.Expr{str, base10, source.Int(16)},
}},
}
@@ -613,11 +594,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
X: ast.NewIdent("strconv"),
Sel: ast.NewIdent("ParseUint"),
},
- Args: []ast.Expr{
- str,
- &ast.BasicLit{Value: "10", Kind: token.INT},
- &ast.BasicLit{Value: "32", Kind: token.INT},
- },
+ Args: []ast.Expr{str, base10, source.Int(32)},
}},
}
@@ -641,11 +618,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
X: ast.NewIdent("strconv"),
Sel: ast.NewIdent("ParseUint"),
},
- Args: []ast.Expr{
- str,
- &ast.BasicLit{Value: "10", Kind: token.INT},
- &ast.BasicLit{Value: "64", Kind: token.INT},
- },
+ Args: []ast.Expr{str, base10, source.Int(64)},
}},
}
@@ -661,11 +634,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
X: ast.NewIdent("strconv"),
Sel: ast.NewIdent("ParseUint"),
},
- Args: []ast.Expr{
- str,
- &ast.BasicLit{Value: "10", Kind: token.INT},
- &ast.BasicLit{Value: "8", Kind: token.INT},
- },
+ Args: []ast.Expr{str, base10, source.Int(8)},
}},
}
diff --git a/generate_test.go b/generate_test.go
index 8d9679d..415830a 100644
--- a/generate_test.go
+++ b/generate_test.go
@@ -452,7 +452,7 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
execute(response, request, true, "GET /bool/{value} PassBool(value)", http.StatusOK, data)
})
mux.HandleFunc("GET /int/{value}", func(response http.ResponseWriter, request *http.Request) {
- valueParsed, err := strconv.ParseInt(request.PathValue("value"), 10, 64)
+ valueParsed, err := strconv.Atoi(request.PathValue("value"))
if err != nil {
http.Error(response, err.Error(), http.StatusBadRequest)
return
diff --git a/internal/source/go.go b/internal/source/go.go
index 2326ed3..9bb9b56 100644
--- a/internal/source/go.go
+++ b/internal/source/go.go
@@ -222,3 +222,5 @@ func HTTPStatusCode(pkg string, n int) ast.Expr {
Sel: ast.NewIdent(ident),
}
}
+
+func Int(n int) *ast.BasicLit { return &ast.BasicLit{Value: strconv.Itoa(n), Kind: token.INT} }
From 1af4420ae95afc9ff981caca0d7be710ff1f88c8 Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Mon, 26 Aug 2024 18:42:30 -0700
Subject: [PATCH 11/26] refactor: move token out of assign func
---
generate.go | 31 ++++++++++++-------------------
1 file changed, 12 insertions(+), 19 deletions(-)
diff --git a/generate.go b/generate.go
index 598e0b1..cd2e94f 100644
--- a/generate.go
+++ b/generate.go
@@ -173,7 +173,7 @@ func (def TemplateName) funcLit(method *ast.FuncType) (*ast.FuncLit, []*ast.Impo
},
Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(arg.Name)}},
}
- statements, parseImports, err := httpPathValueAssignment(method, i, arg, errVar, src, errCheck)
+ statements, parseImports, err := httpPathValueAssignment(method, i, arg, errVar, src, token.DEFINE, errCheck)
if err != nil {
return nil, nil, err
}
@@ -391,7 +391,7 @@ func contextAssignment() *ast.AssignStmt {
}
}
-func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident, str ast.Expr, errCheck *ast.IfStmt) ([]ast.Stmt, []*ast.ImportSpec, error) {
+func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident, str ast.Expr, assignTok token.Token, errCheck *ast.IfStmt) ([]ast.Stmt, []*ast.ImportSpec, error) {
const parsedVarSuffix = "Parsed"
for typeIndex, typeExp := range source.IterateFieldTypes(method.Params.List) {
if typeIndex != i {
@@ -436,7 +436,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
assign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
- Tok: token.DEFINE,
+ Tok: assignTok,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent(paramTypeIdent.Name),
Args: []ast.Expr{ast.NewIdent(tmp)},
@@ -461,7 +461,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
assign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
- Tok: token.DEFINE,
+ Tok: assignTok,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent(paramTypeIdent.Name),
Args: []ast.Expr{ast.NewIdent(tmp)},
@@ -486,7 +486,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
assign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
- Tok: token.DEFINE,
+ Tok: assignTok,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent(paramTypeIdent.Name),
Args: []ast.Expr{ast.NewIdent(tmp)},
@@ -511,7 +511,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
assign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
- Tok: token.DEFINE,
+ Tok: assignTok,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent(paramTypeIdent.Name),
Args: []ast.Expr{ast.NewIdent(tmp)},
@@ -550,7 +550,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
assign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
- Tok: token.DEFINE,
+ Tok: assignTok,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent(paramTypeIdent.Name),
Args: []ast.Expr{ast.NewIdent(tmp)},
@@ -575,7 +575,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
assign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
- Tok: token.DEFINE,
+ Tok: assignTok,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent(paramTypeIdent.Name),
Args: []ast.Expr{ast.NewIdent(tmp)},
@@ -600,7 +600,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
assign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
- Tok: token.DEFINE,
+ Tok: assignTok,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent(paramTypeIdent.Name),
Args: []ast.Expr{ast.NewIdent(tmp)},
@@ -640,7 +640,7 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
assign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
- Tok: token.DEFINE,
+ Tok: assignTok,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent(paramTypeIdent.Name),
Args: []ast.Expr{ast.NewIdent(tmp)},
@@ -651,16 +651,9 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident
case "string":
assign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
- Tok: token.DEFINE,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
- Sel: ast.NewIdent(requestPathValue),
- },
- Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(arg.Name)}},
- }},
+ Tok: assignTok,
+ Rhs: []ast.Expr{str},
}
-
return []ast.Stmt{assign}, nil, nil
}
}
From e9368d41a31996d9674c606d97acbc96ba62031e Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Mon, 26 Aug 2024 18:47:54 -0700
Subject: [PATCH 12/26] refactor: move parse statement generator to new func
---
generate.go | 466 ++++++++++++++++++++++++++--------------------------
1 file changed, 235 insertions(+), 231 deletions(-)
diff --git a/generate.go b/generate.go
index cd2e94f..659e205 100644
--- a/generate.go
+++ b/generate.go
@@ -392,272 +392,276 @@ func contextAssignment() *ast.AssignStmt {
}
func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident, str ast.Expr, assignTok token.Token, errCheck *ast.IfStmt) ([]ast.Stmt, []*ast.ImportSpec, error) {
- const parsedVarSuffix = "Parsed"
for typeIndex, typeExp := range source.IterateFieldTypes(method.Params.List) {
if typeIndex != i {
continue
}
- paramTypeIdent, ok := typeExp.(*ast.Ident)
- if !ok {
- return nil, nil, fmt.Errorf("unsupported type: %s", source.Format(typeExp))
+ return parseStringStatements(arg.Name, ast.NewIdent(arg.Name), errVar, str, typeExp, assignTok, errCheck)
+ }
+ return nil, nil, fmt.Errorf("type for argumement %d not found", i)
+}
+
+func parseStringStatements(name string, result ast.Expr, errVar *ast.Ident, str, typeExp ast.Expr, assignTok token.Token, errCheck *ast.IfStmt) ([]ast.Stmt, []*ast.ImportSpec, error) {
+ const parsedVarSuffix = "Parsed"
+ paramTypeIdent, ok := typeExp.(*ast.Ident)
+ if !ok {
+ return nil, nil, fmt.Errorf("unsupported type: %s", source.Format(typeExp))
+ }
+ base10 := source.Int(10)
+ switch paramTypeIdent.Name {
+ default:
+ return nil, nil, fmt.Errorf("method param type %s not supported", source.Format(typeExp))
+ case "bool":
+ assign := &ast.AssignStmt{
+ Lhs: []ast.Expr{result, ast.NewIdent(errVar.Name)},
+ Tok: token.DEFINE,
+ Rhs: []ast.Expr{&ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent("strconv"),
+ Sel: ast.NewIdent("ParseBool"),
+ },
+ Args: []ast.Expr{str},
+ }},
}
- base10 := source.Int(10)
- switch paramTypeIdent.Name {
- default:
- return nil, nil, fmt.Errorf("method param type %s not supported", source.Format(typeExp))
- case "bool":
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(arg.Name), ast.NewIdent(errVar.Name)},
- Tok: token.DEFINE,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent("strconv"),
- Sel: ast.NewIdent("ParseBool"),
- },
- Args: []ast.Expr{str},
- }},
- }
- return []ast.Stmt{assign, errCheck}, []*ast.ImportSpec{importSpec("strconv")}, nil
- case "int":
- tmp := arg.Name + parsedVarSuffix
+ return []ast.Stmt{assign, errCheck}, []*ast.ImportSpec{importSpec("strconv")}, nil
+ case "int":
+ tmp := name + parsedVarSuffix
- parse := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
- Tok: token.DEFINE,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent("strconv"),
- Sel: ast.NewIdent("Atoi"),
- },
- Args: []ast.Expr{str},
- }},
- }
+ parse := &ast.AssignStmt{
+ Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
+ Tok: token.DEFINE,
+ Rhs: []ast.Expr{&ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent("strconv"),
+ Sel: ast.NewIdent("Atoi"),
+ },
+ Args: []ast.Expr{str},
+ }},
+ }
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
- Tok: assignTok,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: ast.NewIdent(paramTypeIdent.Name),
- Args: []ast.Expr{ast.NewIdent(tmp)},
- }},
- }
+ assign := &ast.AssignStmt{
+ Lhs: []ast.Expr{result},
+ Tok: assignTok,
+ Rhs: []ast.Expr{&ast.CallExpr{
+ Fun: ast.NewIdent(paramTypeIdent.Name),
+ Args: []ast.Expr{ast.NewIdent(tmp)},
+ }},
+ }
- return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
- case "int16":
- tmp := arg.Name + parsedVarSuffix
+ return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
+ case "int16":
+ tmp := name + parsedVarSuffix
- parse := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
- Tok: token.DEFINE,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent("strconv"),
- Sel: ast.NewIdent("ParseInt"),
- },
- Args: []ast.Expr{str, base10, source.Int(16)},
- }},
- }
+ parse := &ast.AssignStmt{
+ Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
+ Tok: token.DEFINE,
+ Rhs: []ast.Expr{&ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent("strconv"),
+ Sel: ast.NewIdent("ParseInt"),
+ },
+ Args: []ast.Expr{str, base10, source.Int(16)},
+ }},
+ }
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
- Tok: assignTok,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: ast.NewIdent(paramTypeIdent.Name),
- Args: []ast.Expr{ast.NewIdent(tmp)},
- }},
- }
+ assign := &ast.AssignStmt{
+ Lhs: []ast.Expr{result},
+ Tok: assignTok,
+ Rhs: []ast.Expr{&ast.CallExpr{
+ Fun: ast.NewIdent(paramTypeIdent.Name),
+ Args: []ast.Expr{ast.NewIdent(tmp)},
+ }},
+ }
- return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
- case "int32":
- tmp := arg.Name + parsedVarSuffix
+ return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
+ case "int32":
+ tmp := name + parsedVarSuffix
- parse := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
- Tok: token.DEFINE,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent("strconv"),
- Sel: ast.NewIdent("ParseInt"),
- },
- Args: []ast.Expr{str, base10, source.Int(32)},
- }},
- }
+ parse := &ast.AssignStmt{
+ Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
+ Tok: token.DEFINE,
+ Rhs: []ast.Expr{&ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent("strconv"),
+ Sel: ast.NewIdent("ParseInt"),
+ },
+ Args: []ast.Expr{str, base10, source.Int(32)},
+ }},
+ }
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
- Tok: assignTok,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: ast.NewIdent(paramTypeIdent.Name),
- Args: []ast.Expr{ast.NewIdent(tmp)},
- }},
- }
+ assign := &ast.AssignStmt{
+ Lhs: []ast.Expr{result},
+ Tok: assignTok,
+ Rhs: []ast.Expr{&ast.CallExpr{
+ Fun: ast.NewIdent(paramTypeIdent.Name),
+ Args: []ast.Expr{ast.NewIdent(tmp)},
+ }},
+ }
- return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
- case "int8":
- tmp := arg.Name + parsedVarSuffix
+ return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
+ case "int8":
+ tmp := name + parsedVarSuffix
- parse := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
- Tok: token.DEFINE,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent("strconv"),
- Sel: ast.NewIdent("ParseInt"),
- },
- Args: []ast.Expr{str, base10, source.Int(8)},
- }},
- }
+ parse := &ast.AssignStmt{
+ Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
+ Tok: token.DEFINE,
+ Rhs: []ast.Expr{&ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent("strconv"),
+ Sel: ast.NewIdent("ParseInt"),
+ },
+ Args: []ast.Expr{str, base10, source.Int(8)},
+ }},
+ }
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
- Tok: assignTok,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: ast.NewIdent(paramTypeIdent.Name),
- Args: []ast.Expr{ast.NewIdent(tmp)},
- }},
- }
+ assign := &ast.AssignStmt{
+ Lhs: []ast.Expr{result},
+ Tok: assignTok,
+ Rhs: []ast.Expr{&ast.CallExpr{
+ Fun: ast.NewIdent(paramTypeIdent.Name),
+ Args: []ast.Expr{ast.NewIdent(tmp)},
+ }},
+ }
- return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
- case "int64":
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(arg.Name), ast.NewIdent(errVar.Name)},
- Tok: token.DEFINE,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent("strconv"),
- Sel: ast.NewIdent("ParseInt"),
- },
- Args: []ast.Expr{str, base10, source.Int(64)},
- }},
- }
+ return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
+ case "int64":
+ assign := &ast.AssignStmt{
+ Lhs: []ast.Expr{result, ast.NewIdent(errVar.Name)},
+ Tok: token.DEFINE,
+ Rhs: []ast.Expr{&ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent("strconv"),
+ Sel: ast.NewIdent("ParseInt"),
+ },
+ Args: []ast.Expr{str, base10, source.Int(64)},
+ }},
+ }
- return []ast.Stmt{assign, errCheck}, []*ast.ImportSpec{importSpec("strconv")}, nil
- case "uint":
- tmp := arg.Name + parsedVarSuffix
+ return []ast.Stmt{assign, errCheck}, []*ast.ImportSpec{importSpec("strconv")}, nil
+ case "uint":
+ tmp := name + parsedVarSuffix
- parse := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
- Tok: token.DEFINE,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent("strconv"),
- Sel: ast.NewIdent("ParseUint"),
- },
- Args: []ast.Expr{str, base10, source.Int(64)},
- }},
- }
+ parse := &ast.AssignStmt{
+ Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
+ Tok: token.DEFINE,
+ Rhs: []ast.Expr{&ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent("strconv"),
+ Sel: ast.NewIdent("ParseUint"),
+ },
+ Args: []ast.Expr{str, base10, source.Int(64)},
+ }},
+ }
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
- Tok: assignTok,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: ast.NewIdent(paramTypeIdent.Name),
- Args: []ast.Expr{ast.NewIdent(tmp)},
- }},
- }
+ assign := &ast.AssignStmt{
+ Lhs: []ast.Expr{result},
+ Tok: assignTok,
+ Rhs: []ast.Expr{&ast.CallExpr{
+ Fun: ast.NewIdent(paramTypeIdent.Name),
+ Args: []ast.Expr{ast.NewIdent(tmp)},
+ }},
+ }
- return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
- case "uint16":
- tmp := arg.Name + parsedVarSuffix
+ return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
+ case "uint16":
+ tmp := name + parsedVarSuffix
- parse := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
- Tok: token.DEFINE,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent("strconv"),
- Sel: ast.NewIdent("ParseUint"),
- },
- Args: []ast.Expr{str, base10, source.Int(16)},
- }},
- }
+ parse := &ast.AssignStmt{
+ Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
+ Tok: token.DEFINE,
+ Rhs: []ast.Expr{&ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent("strconv"),
+ Sel: ast.NewIdent("ParseUint"),
+ },
+ Args: []ast.Expr{str, base10, source.Int(16)},
+ }},
+ }
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
- Tok: assignTok,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: ast.NewIdent(paramTypeIdent.Name),
- Args: []ast.Expr{ast.NewIdent(tmp)},
- }},
- }
+ assign := &ast.AssignStmt{
+ Lhs: []ast.Expr{result},
+ Tok: assignTok,
+ Rhs: []ast.Expr{&ast.CallExpr{
+ Fun: ast.NewIdent(paramTypeIdent.Name),
+ Args: []ast.Expr{ast.NewIdent(tmp)},
+ }},
+ }
- return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
- case "uint32":
- tmp := arg.Name + parsedVarSuffix
+ return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
+ case "uint32":
+ tmp := name + parsedVarSuffix
- parse := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
- Tok: token.DEFINE,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent("strconv"),
- Sel: ast.NewIdent("ParseUint"),
- },
- Args: []ast.Expr{str, base10, source.Int(32)},
- }},
- }
+ parse := &ast.AssignStmt{
+ Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
+ Tok: token.DEFINE,
+ Rhs: []ast.Expr{&ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent("strconv"),
+ Sel: ast.NewIdent("ParseUint"),
+ },
+ Args: []ast.Expr{str, base10, source.Int(32)},
+ }},
+ }
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
- Tok: assignTok,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: ast.NewIdent(paramTypeIdent.Name),
- Args: []ast.Expr{ast.NewIdent(tmp)},
- }},
- }
+ assign := &ast.AssignStmt{
+ Lhs: []ast.Expr{result},
+ Tok: assignTok,
+ Rhs: []ast.Expr{&ast.CallExpr{
+ Fun: ast.NewIdent(paramTypeIdent.Name),
+ Args: []ast.Expr{ast.NewIdent(tmp)},
+ }},
+ }
- return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
- case "uint64":
+ return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
+ case "uint64":
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(arg.Name), ast.NewIdent(errVar.Name)},
- Tok: token.DEFINE,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent("strconv"),
- Sel: ast.NewIdent("ParseUint"),
- },
- Args: []ast.Expr{str, base10, source.Int(64)},
- }},
- }
+ assign := &ast.AssignStmt{
+ Lhs: []ast.Expr{result, ast.NewIdent(errVar.Name)},
+ Tok: token.DEFINE,
+ Rhs: []ast.Expr{&ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent("strconv"),
+ Sel: ast.NewIdent("ParseUint"),
+ },
+ Args: []ast.Expr{str, base10, source.Int(64)},
+ }},
+ }
- return []ast.Stmt{assign, errCheck}, []*ast.ImportSpec{importSpec("strconv")}, nil
- case "uint8":
- tmp := arg.Name + parsedVarSuffix
+ return []ast.Stmt{assign, errCheck}, []*ast.ImportSpec{importSpec("strconv")}, nil
+ case "uint8":
+ tmp := name + parsedVarSuffix
- parse := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
- Tok: token.DEFINE,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent("strconv"),
- Sel: ast.NewIdent("ParseUint"),
- },
- Args: []ast.Expr{str, base10, source.Int(8)},
- }},
- }
+ parse := &ast.AssignStmt{
+ Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
+ Tok: token.DEFINE,
+ Rhs: []ast.Expr{&ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent("strconv"),
+ Sel: ast.NewIdent("ParseUint"),
+ },
+ Args: []ast.Expr{str, base10, source.Int(8)},
+ }},
+ }
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
- Tok: assignTok,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: ast.NewIdent(paramTypeIdent.Name),
- Args: []ast.Expr{ast.NewIdent(tmp)},
- }},
- }
+ assign := &ast.AssignStmt{
+ Lhs: []ast.Expr{result},
+ Tok: assignTok,
+ Rhs: []ast.Expr{&ast.CallExpr{
+ Fun: ast.NewIdent(paramTypeIdent.Name),
+ Args: []ast.Expr{ast.NewIdent(tmp)},
+ }},
+ }
- return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
- case "string":
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(arg.Name)},
- Tok: assignTok,
- Rhs: []ast.Expr{str},
- }
- return []ast.Stmt{assign}, nil, nil
+ return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
+ case "string":
+ assign := &ast.AssignStmt{
+ Lhs: []ast.Expr{result},
+ Tok: assignTok,
+ Rhs: []ast.Expr{str},
}
+ return []ast.Stmt{assign}, nil, nil
}
- return nil, nil, fmt.Errorf("type for argumement %d not found", i)
}
func paramParseError(errVar *ast.Ident) *ast.IfStmt {
From 3861359ee24f4e36eb1eb69249bf025228cd87fb Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Mon, 26 Aug 2024 22:38:57 -0700
Subject: [PATCH 13/26] refactor: make parsing strings more general
---
generate.go | 75 +++++++++++++++++--------------------------
internal/source/go.go | 7 ++++
2 files changed, 37 insertions(+), 45 deletions(-)
diff --git a/generate.go b/generate.go
index 659e205..88e9c43 100644
--- a/generate.go
+++ b/generate.go
@@ -164,8 +164,21 @@ func (def TemplateName) funcLit(method *ast.FuncType) (*ast.FuncLit, []*ast.Impo
call.Args = append(call.Args, ast.NewIdent(TemplateNameScopeIdentifierContext))
imports = append(imports, importSpec("context"))
default:
- errVar := ast.NewIdent("err")
- errCheck := paramParseError(errVar)
+ const errVarIdent = "err"
+ errCheck := source.ErrorCheckReturn(errVarIdent, &ast.ExprStmt{X: &ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent(httpPackageIdent),
+ Sel: ast.NewIdent("Error"),
+ },
+ Args: []ast.Expr{
+ ast.NewIdent(httpResponseField().Names[0].Name),
+ &ast.CallExpr{
+ Fun: &ast.SelectorExpr{X: ast.NewIdent("err"), Sel: ast.NewIdent("Error")},
+ Args: []ast.Expr{},
+ },
+ source.HTTPStatusCode(httpPackageIdent, http.StatusBadRequest),
+ },
+ }}, &ast.ReturnStmt{})
src := &ast.CallExpr{
Fun: &ast.SelectorExpr{
X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
@@ -173,7 +186,7 @@ func (def TemplateName) funcLit(method *ast.FuncType) (*ast.FuncLit, []*ast.Impo
},
Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(arg.Name)}},
}
- statements, parseImports, err := httpPathValueAssignment(method, i, arg, errVar, src, token.DEFINE, errCheck)
+ statements, parseImports, err := httpPathValueAssignment(method, i, arg, errVarIdent, src, token.DEFINE, errCheck)
if err != nil {
return nil, nil, err
}
@@ -391,17 +404,17 @@ func contextAssignment() *ast.AssignStmt {
}
}
-func httpPathValueAssignment(method *ast.FuncType, i int, arg, errVar *ast.Ident, str ast.Expr, assignTok token.Token, errCheck *ast.IfStmt) ([]ast.Stmt, []*ast.ImportSpec, error) {
+func httpPathValueAssignment(method *ast.FuncType, i int, arg *ast.Ident, errVarIdent string, str ast.Expr, assignTok token.Token, errCheck *ast.IfStmt) ([]ast.Stmt, []*ast.ImportSpec, error) {
for typeIndex, typeExp := range source.IterateFieldTypes(method.Params.List) {
if typeIndex != i {
continue
}
- return parseStringStatements(arg.Name, ast.NewIdent(arg.Name), errVar, str, typeExp, assignTok, errCheck)
+ return parseStringStatements(arg.Name, ast.NewIdent(arg.Name), errVarIdent, str, typeExp, assignTok, errCheck)
}
return nil, nil, fmt.Errorf("type for argumement %d not found", i)
}
-func parseStringStatements(name string, result ast.Expr, errVar *ast.Ident, str, typeExp ast.Expr, assignTok token.Token, errCheck *ast.IfStmt) ([]ast.Stmt, []*ast.ImportSpec, error) {
+func parseStringStatements(name string, result ast.Expr, errVarIdent string, str, typeExp ast.Expr, assignTok token.Token, errCheck *ast.IfStmt) ([]ast.Stmt, []*ast.ImportSpec, error) {
const parsedVarSuffix = "Parsed"
paramTypeIdent, ok := typeExp.(*ast.Ident)
if !ok {
@@ -413,7 +426,7 @@ func parseStringStatements(name string, result ast.Expr, errVar *ast.Ident, str,
return nil, nil, fmt.Errorf("method param type %s not supported", source.Format(typeExp))
case "bool":
assign := &ast.AssignStmt{
- Lhs: []ast.Expr{result, ast.NewIdent(errVar.Name)},
+ Lhs: []ast.Expr{result, ast.NewIdent(errVarIdent)},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: &ast.SelectorExpr{
@@ -429,7 +442,7 @@ func parseStringStatements(name string, result ast.Expr, errVar *ast.Ident, str,
tmp := name + parsedVarSuffix
parse := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
+ Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVarIdent)},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: &ast.SelectorExpr{
@@ -454,7 +467,7 @@ func parseStringStatements(name string, result ast.Expr, errVar *ast.Ident, str,
tmp := name + parsedVarSuffix
parse := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
+ Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVarIdent)},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: &ast.SelectorExpr{
@@ -479,7 +492,7 @@ func parseStringStatements(name string, result ast.Expr, errVar *ast.Ident, str,
tmp := name + parsedVarSuffix
parse := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
+ Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVarIdent)},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: &ast.SelectorExpr{
@@ -504,7 +517,7 @@ func parseStringStatements(name string, result ast.Expr, errVar *ast.Ident, str,
tmp := name + parsedVarSuffix
parse := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
+ Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVarIdent)},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: &ast.SelectorExpr{
@@ -527,7 +540,7 @@ func parseStringStatements(name string, result ast.Expr, errVar *ast.Ident, str,
return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "int64":
assign := &ast.AssignStmt{
- Lhs: []ast.Expr{result, ast.NewIdent(errVar.Name)},
+ Lhs: []ast.Expr{result, ast.NewIdent(errVarIdent)},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: &ast.SelectorExpr{
@@ -543,7 +556,7 @@ func parseStringStatements(name string, result ast.Expr, errVar *ast.Ident, str,
tmp := name + parsedVarSuffix
parse := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
+ Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVarIdent)},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: &ast.SelectorExpr{
@@ -568,7 +581,7 @@ func parseStringStatements(name string, result ast.Expr, errVar *ast.Ident, str,
tmp := name + parsedVarSuffix
parse := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
+ Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVarIdent)},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: &ast.SelectorExpr{
@@ -593,7 +606,7 @@ func parseStringStatements(name string, result ast.Expr, errVar *ast.Ident, str,
tmp := name + parsedVarSuffix
parse := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
+ Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVarIdent)},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: &ast.SelectorExpr{
@@ -617,7 +630,7 @@ func parseStringStatements(name string, result ast.Expr, errVar *ast.Ident, str,
case "uint64":
assign := &ast.AssignStmt{
- Lhs: []ast.Expr{result, ast.NewIdent(errVar.Name)},
+ Lhs: []ast.Expr{result, ast.NewIdent(errVarIdent)},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: &ast.SelectorExpr{
@@ -633,7 +646,7 @@ func parseStringStatements(name string, result ast.Expr, errVar *ast.Ident, str,
tmp := name + parsedVarSuffix
parse := &ast.AssignStmt{
- Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVar.Name)},
+ Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVarIdent)},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: &ast.SelectorExpr{
@@ -664,34 +677,6 @@ func parseStringStatements(name string, result ast.Expr, errVar *ast.Ident, str,
}
}
-func paramParseError(errVar *ast.Ident) *ast.IfStmt {
- return &ast.IfStmt{
- Cond: &ast.BinaryExpr{X: ast.NewIdent(errVar.Name), Op: token.NEQ, Y: ast.NewIdent("nil")},
- Body: &ast.BlockStmt{
- List: []ast.Stmt{
- &ast.ExprStmt{X: &ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent(httpPackageIdent),
- Sel: ast.NewIdent("Error"),
- },
- Args: []ast.Expr{
- ast.NewIdent(httpResponseField().Names[0].Name),
- &ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent("err"),
- Sel: ast.NewIdent("Error"),
- },
- Args: []ast.Expr{},
- },
- source.HTTPStatusCode(httpPackageIdent, http.StatusBadRequest),
- },
- }},
- &ast.ReturnStmt{},
- },
- },
- }
-}
-
func (def TemplateName) executeCall(status, data ast.Expr, writeHeader bool) *ast.ExprStmt {
return &ast.ExprStmt{X: &ast.CallExpr{
Fun: ast.NewIdent(executeIdentName),
diff --git a/internal/source/go.go b/internal/source/go.go
index 9bb9b56..80a8ef7 100644
--- a/internal/source/go.go
+++ b/internal/source/go.go
@@ -224,3 +224,10 @@ func HTTPStatusCode(pkg string, n int) ast.Expr {
}
func Int(n int) *ast.BasicLit { return &ast.BasicLit{Value: strconv.Itoa(n), Kind: token.INT} }
+
+func ErrorCheckReturn(errVarIdent string, body ...ast.Stmt) *ast.IfStmt {
+ return &ast.IfStmt{
+ Cond: &ast.BinaryExpr{X: ast.NewIdent(errVarIdent), Op: token.NEQ, Y: ast.NewIdent("nil")},
+ Body: &ast.BlockStmt{List: body},
+ }
+}
From be77fa1cb4ff4d1dcfef4d4cdd8b76d04424d8ba Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Wed, 28 Aug 2024 16:50:01 -0700
Subject: [PATCH 14/26] add form declaration to func literal
---
generate.go | 18 +++++++++++++++++
generate_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++
internal/source/go.go | 14 ++++++++++++++
name.go | 2 ++
4 files changed, 79 insertions(+)
diff --git a/generate.go b/generate.go
index 88e9c43..5030387 100644
--- a/generate.go
+++ b/generate.go
@@ -163,6 +163,10 @@ func (def TemplateName) funcLit(method *ast.FuncType) (*ast.FuncLit, []*ast.Impo
lit.Body.List = append(lit.Body.List, contextAssignment())
call.Args = append(call.Args, ast.NewIdent(TemplateNameScopeIdentifierContext))
imports = append(imports, importSpec("context"))
+ case TemplateNameScopeIdentifierForm:
+ _, tp, _ := source.FieldIndex(method.Params.List, i)
+ lit.Body.List = append(lit.Body.List, formDeclaration(arg.Name, tp))
+ call.Args = append(call.Args, ast.NewIdent(arg.Name))
default:
const errVarIdent = "err"
errCheck := source.ErrorCheckReturn(errVarIdent, &ast.ExprStmt{X: &ast.CallExpr{
@@ -404,6 +408,20 @@ func contextAssignment() *ast.AssignStmt {
}
}
+func formDeclaration(ident string, typeExp ast.Expr) *ast.DeclStmt {
+ return &ast.DeclStmt{
+ Decl: &ast.GenDecl{
+ Tok: token.VAR,
+ Specs: []ast.Spec{
+ &ast.ValueSpec{
+ Names: []*ast.Ident{ast.NewIdent(ident)},
+ Type: typeExp,
+ },
+ },
+ },
+ }
+}
+
func httpPathValueAssignment(method *ast.FuncType, i int, arg *ast.Ident, errVarIdent string, str ast.Expr, assignTok token.Token, errCheck *ast.IfStmt) ([]ast.Stmt, []*ast.ImportSpec, error) {
for typeIndex, typeExp := range source.IterateFieldTypes(method.Params.List) {
if typeIndex != i {
diff --git a/generate_test.go b/generate_test.go
index 415830a..d8b902c 100644
--- a/generate_test.go
+++ b/generate_test.go
@@ -561,6 +561,51 @@ func execute(response http.ResponseWriter, request *http.Request, writeHeader bo
}
_, _ = buf.WriteTo(response)
}
+`,
+ },
+ {
+ Name: "form has no fields",
+ Templates: `{{define "GET / F(form)"}}Hello, {{.}}!{{end}}`,
+ ReceiverPackage: `
+-- in.go --
+package main
+
+type T struct{}
+
+type In struct{}
+
+func (T) F(form In) any { return nil }
+`,
+ Receiver: "T",
+ ExpectedFile: `package main
+
+import (
+ "net/http"
+ "bytes"
+)
+
+type RoutesReceiver interface {
+ F(form In) any
+}
+
+func routes(mux *http.ServeMux, receiver RoutesReceiver) {
+ mux.HandleFunc("GET /", func(response http.ResponseWriter, request *http.Request) {
+ var form In
+ data := receiver.F(form)
+ execute(response, request, true, "GET / F(form)", http.StatusOK, data)
+ })
+}
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
+ buf := bytes.NewBuffer(nil)
+ if err := templates.ExecuteTemplate(buf, name, data); err != nil {
+ http.Error(response, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ if writeHeader {
+ response.WriteHeader(code)
+ }
+ _, _ = buf.WriteTo(response)
+}
`,
},
} {
diff --git a/internal/source/go.go b/internal/source/go.go
index 80a8ef7..ef78702 100644
--- a/internal/source/go.go
+++ b/internal/source/go.go
@@ -231,3 +231,17 @@ func ErrorCheckReturn(errVarIdent string, body ...ast.Stmt) *ast.IfStmt {
Body: &ast.BlockStmt{List: body},
}
}
+
+func FieldIndex(fields []*ast.Field, i int) (*ast.Ident, ast.Expr, bool) {
+ n := 0
+ for _, field := range fields {
+ for _, name := range field.Names {
+ if n != i {
+ n++
+ continue
+ }
+ return name, field.Type, true
+ }
+ }
+ return nil, nil, false
+}
diff --git a/name.go b/name.go
index 90bfbe0..a92d9b9 100644
--- a/name.go
+++ b/name.go
@@ -173,6 +173,7 @@ const (
TemplateNameScopeIdentifierHTTPRequest = "request"
TemplateNameScopeIdentifierHTTPResponse = "response"
TemplateNameScopeIdentifierContext = "ctx"
+ TemplateNameScopeIdentifierForm = "form"
)
func patternScope() []string {
@@ -180,5 +181,6 @@ func patternScope() []string {
TemplateNameScopeIdentifierHTTPRequest,
TemplateNameScopeIdentifierHTTPResponse,
TemplateNameScopeIdentifierContext,
+ TemplateNameScopeIdentifierForm,
}
}
From 141f401ad22b577d20a98968b176bdb9617a7a4a Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Wed, 28 Aug 2024 17:22:42 -0700
Subject: [PATCH 15/26] pass add form arg from request.Form
---
generate.go | 51 ++++++++++++++++++++++++++++++++++++---
generate_internal_test.go | 6 +++--
generate_test.go | 43 +++++++++++++++++++++++++++++++++
3 files changed, 94 insertions(+), 6 deletions(-)
diff --git a/generate.go b/generate.go
index 5030387..2cc79f4 100644
--- a/generate.go
+++ b/generate.go
@@ -59,7 +59,10 @@ func Generate(templateNames []TemplateName, _ *template.Template, packageName, t
importSpec("net/" + httpPackageIdent),
}
for _, name := range templateNames {
- var method *ast.FuncType
+ var (
+ method *ast.FuncType
+ form *ast.StructType
+ )
if name.fun != nil {
for _, funcDecl := range source.IterateFunctions(receiverPackage) {
if !name.matchReceiver(funcDecl, receiverTypeIdent) {
@@ -78,7 +81,7 @@ func Generate(templateNames []TemplateName, _ *template.Template, packageName, t
Type: method,
})
}
- handlerFunc, methodImports, err := name.funcLit(method)
+ handlerFunc, methodImports, err := name.funcLit(method, form)
if err != nil {
return "", err
}
@@ -127,7 +130,7 @@ func (def TemplateName) callHandleFunc(handlerFuncLit *ast.FuncLit) *ast.ExprStm
}}
}
-func (def TemplateName) funcLit(method *ast.FuncType) (*ast.FuncLit, []*ast.ImportSpec, error) {
+func (def TemplateName) funcLit(method *ast.FuncType, _ *ast.StructType) (*ast.FuncLit, []*ast.ImportSpec, error) {
if def.handler == "" {
return def.httpRequestReceiverTemplateHandlerFunc(), nil, nil
}
@@ -165,7 +168,16 @@ func (def TemplateName) funcLit(method *ast.FuncType) (*ast.FuncLit, []*ast.Impo
imports = append(imports, importSpec("context"))
case TemplateNameScopeIdentifierForm:
_, tp, _ := source.FieldIndex(method.Params.List, i)
- lit.Body.List = append(lit.Body.List, formDeclaration(arg.Name, tp))
+ lit.Body.List = append(lit.Body.List,
+ &ast.ExprStmt{X: &ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
+ Sel: ast.NewIdent("ParseForm"),
+ },
+ Args: []ast.Expr{},
+ }},
+ formDeclaration(arg.Name, tp))
+ imports = append(imports, importSpec("net/url"))
call.Args = append(call.Args, ast.NewIdent(arg.Name))
default:
const errVarIdent = "err"
@@ -257,6 +269,8 @@ func (def TemplateName) funcType() (*ast.FuncType, []*ast.ImportSpec) {
case TemplateNameScopeIdentifierContext:
method.Params.List = append(method.Params.List, contextContextField())
imports = append(imports, importSpec(contextPackageIdent))
+ case TemplateNameScopeIdentifierForm:
+ method.Params.List = append(method.Params.List, urlValuesField(arg.Name))
default:
method.Params.List = append(method.Params.List, pathValueField(arg.Name))
}
@@ -312,6 +326,11 @@ func checkArgument(method *ast.FuncType, argIndex int, exp ast.Expr, argType ast
return fmt.Errorf("method expects type %s but %s is %s.%s", source.Format(argType), arg.Name, contextPackageIdent, contextContextTypeIdent)
}
return nil
+ case TemplateNameScopeIdentifierForm:
+ if !matchSelectorIdents(argType, "url", "Values", false) {
+ return fmt.Errorf("method expects type %s but %s is %s.%s", source.Format(argType), arg.Name, "url", "Values")
+ }
+ return nil
default:
for paramIndex, paramType := range source.IterateFieldTypes(method.Params.List) {
if argIndex != paramIndex {
@@ -376,6 +395,13 @@ func routesFuncType(receiverType ast.Expr) *ast.FuncType {
}}
}
+func urlValuesField(ident string) *ast.Field {
+ return &ast.Field{
+ Names: []*ast.Ident{ast.NewIdent(ident)},
+ Type: &ast.SelectorExpr{X: ast.NewIdent("url"), Sel: ast.NewIdent("Values")},
+ }
+}
+
func httpRequestField() *ast.Field {
return &ast.Field{
Names: []*ast.Ident{ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest)},
@@ -409,6 +435,23 @@ func contextAssignment() *ast.AssignStmt {
}
func formDeclaration(ident string, typeExp ast.Expr) *ast.DeclStmt {
+ if matchSelectorIdents(typeExp, "url", "Values", false) {
+ return &ast.DeclStmt{
+ Decl: &ast.GenDecl{
+ Tok: token.VAR,
+ Specs: []ast.Spec{
+ &ast.ValueSpec{
+ Names: []*ast.Ident{ast.NewIdent(ident)},
+ Type: typeExp,
+ Values: []ast.Expr{
+ &ast.SelectorExpr{X: ast.NewIdent(httpResponseField().Names[0].Name), Sel: ast.NewIdent("Form")},
+ },
+ },
+ },
+ },
+ }
+ }
+
return &ast.DeclStmt{
Decl: &ast.GenDecl{
Tok: token.VAR,
diff --git a/generate_internal_test.go b/generate_internal_test.go
index 4c32560..afc09ad 100644
--- a/generate_internal_test.go
+++ b/generate_internal_test.go
@@ -17,6 +17,7 @@ func TestTemplateName_funcLit(t *testing.T) {
Out string
Imports []string
Method *ast.FuncType
+ Form *ast.StructType
}{
{
Name: "get",
@@ -105,7 +106,7 @@ func TestTemplateName_funcLit(t *testing.T) {
pat, err, ok := NewTemplateName(tt.In)
require.True(t, ok)
require.NoError(t, err)
- out, _, err := pat.funcLit(tt.Method)
+ out, _, err := pat.funcLit(tt.Method, tt.Form)
require.NoError(t, err)
assert.Equal(t, tt.Out, source.Format(out))
})
@@ -118,6 +119,7 @@ func TestTemplateName_HandlerFuncLit_err(t *testing.T) {
In string
ErrSub string
Method *ast.FuncType
+ Form *ast.StructType
}{
{
Name: "missing arguments",
@@ -211,7 +213,7 @@ func TestTemplateName_HandlerFuncLit_err(t *testing.T) {
pat, err, ok := NewTemplateName(tt.In)
require.True(t, ok)
require.NoError(t, err)
- _, _, err = pat.funcLit(tt.Method)
+ _, _, err = pat.funcLit(tt.Method, tt.Form)
assert.ErrorContains(t, err, tt.ErrSub)
})
}
diff --git a/generate_test.go b/generate_test.go
index d8b902c..b388484 100644
--- a/generate_test.go
+++ b/generate_test.go
@@ -606,6 +606,49 @@ func execute(response http.ResponseWriter, request *http.Request, writeHeader bo
}
_, _ = buf.WriteTo(response)
}
+`,
+ },
+ {
+ Name: "F is not defined and a form field is passed",
+ Templates: `{{define "GET / F(form)"}}Hello, {{.}}!{{end}}`,
+ ReceiverPackage: `
+-- in.go --
+package main
+
+type T struct{}
+`,
+ Receiver: "T",
+ ExpectedFile: `package main
+
+import (
+ "net/http"
+ "net/url"
+ "bytes"
+)
+
+type RoutesReceiver interface {
+ F(form url.Values) any
+}
+
+func routes(mux *http.ServeMux, receiver RoutesReceiver) {
+ mux.HandleFunc("GET /", func(response http.ResponseWriter, request *http.Request) {
+ request.ParseForm()
+ var form url.Values = response.Form
+ data := receiver.F(form)
+ execute(response, request, true, "GET / F(form)", http.StatusOK, data)
+ })
+}
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
+ buf := bytes.NewBuffer(nil)
+ if err := templates.ExecuteTemplate(buf, name, data); err != nil {
+ http.Error(response, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ if writeHeader {
+ response.WriteHeader(code)
+ }
+ _, _ = buf.WriteTo(response)
+}
`,
},
} {
From 0489c1dcd4408b987b1842b8ec90084766b87bb6 Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Wed, 28 Aug 2024 18:11:59 -0700
Subject: [PATCH 16/26] add field parsing from tag
---
generate.go | 90 ++++++++++++++++++++++++++++-----
generate_internal_test.go | 6 +--
generate_test.go | 101 ++++++++++++++++++++++++++++++++++++++
3 files changed, 182 insertions(+), 15 deletions(-)
diff --git a/generate.go b/generate.go
index 2cc79f4..107e4a0 100644
--- a/generate.go
+++ b/generate.go
@@ -8,6 +8,7 @@ import (
"html/template"
"log"
"net/http"
+ "reflect"
"slices"
"strconv"
"strings"
@@ -59,10 +60,7 @@ func Generate(templateNames []TemplateName, _ *template.Template, packageName, t
importSpec("net/" + httpPackageIdent),
}
for _, name := range templateNames {
- var (
- method *ast.FuncType
- form *ast.StructType
- )
+ var method *ast.FuncType
if name.fun != nil {
for _, funcDecl := range source.IterateFunctions(receiverPackage) {
if !name.matchReceiver(funcDecl, receiverTypeIdent) {
@@ -81,7 +79,7 @@ func Generate(templateNames []TemplateName, _ *template.Template, packageName, t
Type: method,
})
}
- handlerFunc, methodImports, err := name.funcLit(method, form)
+ handlerFunc, methodImports, err := name.funcLit(method, receiverPackage)
if err != nil {
return "", err
}
@@ -130,7 +128,7 @@ func (def TemplateName) callHandleFunc(handlerFuncLit *ast.FuncLit) *ast.ExprStm
}}
}
-func (def TemplateName) funcLit(method *ast.FuncType, _ *ast.StructType) (*ast.FuncLit, []*ast.ImportSpec, error) {
+func (def TemplateName) funcLit(method *ast.FuncType, files []*ast.File) (*ast.FuncLit, []*ast.ImportSpec, error) {
if def.handler == "" {
return def.httpRequestReceiverTemplateHandlerFunc(), nil, nil
}
@@ -139,14 +137,18 @@ func (def TemplateName) funcLit(method *ast.FuncType, _ *ast.StructType) (*ast.F
Body: &ast.BlockStmt{},
}
call := &ast.CallExpr{Fun: callReceiverMethod(def.fun)}
+ var formStruct *ast.StructType
if method != nil {
if method.Params.NumFields() != len(def.call.Args) {
return nil, nil, errWrongNumberOfArguments(def, method)
}
for pi, pt := range fieldListTypes(method.Params) {
- if err := checkArgument(method, pi, def.call.Args[pi], pt); err != nil {
+ if err := checkArgument(method, pi, def.call.Args[pi], pt, files); err != nil {
return nil, nil, err
}
+ if s, ok := findFormStruct(pt, files); ok {
+ formStruct = s
+ }
}
}
var (
@@ -177,7 +179,48 @@ func (def TemplateName) funcLit(method *ast.FuncType, _ *ast.StructType) (*ast.F
Args: []ast.Expr{},
}},
formDeclaration(arg.Name, tp))
- imports = append(imports, importSpec("net/url"))
+ if formStruct != nil {
+ for _, field := range formStruct.Fields.List {
+ var inputNameTag string
+ if field.Tag != nil {
+ v, _ := strconv.Unquote(field.Tag.Value)
+ tags := reflect.StructTag(v)
+ n, hasInputTag := tags.Lookup("input")
+ if hasInputTag {
+ inputNameTag = n
+ }
+ }
+
+ for _, name := range field.Names {
+ inputName := cmp.Or(inputNameTag, name.Name)
+ lit.Body.List = append(lit.Body.List, &ast.AssignStmt{
+ Tok: token.ASSIGN,
+ Lhs: []ast.Expr{
+ &ast.SelectorExpr{
+ X: ast.NewIdent(arg.Name),
+ Sel: ast.NewIdent(name.Name),
+ },
+ },
+ Rhs: []ast.Expr{
+ &ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
+ Sel: ast.NewIdent("FormValue"),
+ },
+ Args: []ast.Expr{
+ &ast.BasicLit{
+ Kind: token.STRING,
+ Value: strconv.Quote(inputName),
+ },
+ },
+ },
+ },
+ })
+ }
+ }
+ } else {
+ imports = append(imports, importSpec("net/url"))
+ }
call.Args = append(call.Args, ast.NewIdent(arg.Name))
default:
const errVarIdent = "err"
@@ -307,7 +350,7 @@ func errWrongNumberOfArguments(def TemplateName, method *ast.FuncType) error {
return fmt.Errorf("handler %s expects %d arguments but call %s has %d", source.Format(&ast.FuncDecl{Name: ast.NewIdent(def.fun.Name), Type: method}), method.Params.NumFields(), def.handler, len(def.call.Args))
}
-func checkArgument(method *ast.FuncType, argIndex int, exp ast.Expr, argType ast.Expr) error {
+func checkArgument(method *ast.FuncType, argIndex int, exp ast.Expr, argType ast.Expr, files []*ast.File) error {
// TODO: rewrite to "cannot use 32 (untyped int constant) as string value in argument to strings.ToUpper"
arg := exp.(*ast.Ident)
switch arg.Name {
@@ -327,8 +370,12 @@ func checkArgument(method *ast.FuncType, argIndex int, exp ast.Expr, argType ast
}
return nil
case TemplateNameScopeIdentifierForm:
- if !matchSelectorIdents(argType, "url", "Values", false) {
- return fmt.Errorf("method expects type %s but %s is %s.%s", source.Format(argType), arg.Name, "url", "Values")
+ if matchSelectorIdents(argType, "url", "Values", false) {
+ return nil
+ }
+ _, ok := findFormStruct(argType, files)
+ if !ok {
+ return fmt.Errorf("method expects form to have type url.Values or T (where T is some struct type)")
}
return nil
default:
@@ -347,6 +394,27 @@ func checkArgument(method *ast.FuncType, argIndex int, exp ast.Expr, argType ast
}
}
+func findFormStruct(argType ast.Expr, files []*ast.File) (*ast.StructType, bool) {
+ if argTypeIdent, ok := argType.(*ast.Ident); ok {
+ for _, file := range files {
+ for _, d := range file.Decls {
+ decl, ok := d.(*ast.GenDecl)
+ if !ok || decl.Tok != token.TYPE {
+ continue
+ }
+ for _, s := range decl.Specs {
+ spec := s.(*ast.TypeSpec)
+ structType, isStruct := spec.Type.(*ast.StructType)
+ if isStruct && spec.Name.Name == argTypeIdent.Name {
+ return structType, true
+ }
+ }
+ }
+ }
+ }
+ return nil, false
+}
+
func matchSelectorIdents(expr ast.Expr, pkg, name string, star bool) bool {
if star {
st, ok := expr.(*ast.StarExpr)
diff --git a/generate_internal_test.go b/generate_internal_test.go
index afc09ad..ecf02e1 100644
--- a/generate_internal_test.go
+++ b/generate_internal_test.go
@@ -17,7 +17,6 @@ func TestTemplateName_funcLit(t *testing.T) {
Out string
Imports []string
Method *ast.FuncType
- Form *ast.StructType
}{
{
Name: "get",
@@ -106,7 +105,7 @@ func TestTemplateName_funcLit(t *testing.T) {
pat, err, ok := NewTemplateName(tt.In)
require.True(t, ok)
require.NoError(t, err)
- out, _, err := pat.funcLit(tt.Method, tt.Form)
+ out, _, err := pat.funcLit(tt.Method, nil)
require.NoError(t, err)
assert.Equal(t, tt.Out, source.Format(out))
})
@@ -119,7 +118,6 @@ func TestTemplateName_HandlerFuncLit_err(t *testing.T) {
In string
ErrSub string
Method *ast.FuncType
- Form *ast.StructType
}{
{
Name: "missing arguments",
@@ -213,7 +211,7 @@ func TestTemplateName_HandlerFuncLit_err(t *testing.T) {
pat, err, ok := NewTemplateName(tt.In)
require.True(t, ok)
require.NoError(t, err)
- _, _, err = pat.funcLit(tt.Method, tt.Form)
+ _, _, err = pat.funcLit(tt.Method, nil)
assert.ErrorContains(t, err, tt.ErrSub)
})
}
diff --git a/generate_test.go b/generate_test.go
index b388484..07f4b28 100644
--- a/generate_test.go
+++ b/generate_test.go
@@ -590,6 +590,7 @@ type RoutesReceiver interface {
func routes(mux *http.ServeMux, receiver RoutesReceiver) {
mux.HandleFunc("GET /", func(response http.ResponseWriter, request *http.Request) {
+ request.ParseForm()
var form In
data := receiver.F(form)
execute(response, request, true, "GET / F(form)", http.StatusOK, data)
@@ -649,6 +650,106 @@ func execute(response http.ResponseWriter, request *http.Request, writeHeader bo
}
_, _ = buf.WriteTo(response)
}
+`,
+ },
+ {
+ Name: "F is defined and form type is a struct",
+ Templates: `{{define "GET / F(form)"}}Hello, {{.}}!{{end}}`,
+ ReceiverPackage: `
+-- in.go --
+package main
+
+type (
+ T struct{}
+ In struct{
+ field string
+ }
+)
+
+func (T) F(form In) int { return 0 }
+`,
+ Receiver: "T",
+ ExpectedFile: `package main
+
+import (
+ "net/http"
+ "bytes"
+)
+
+type RoutesReceiver interface {
+ F(form In) int
+}
+
+func routes(mux *http.ServeMux, receiver RoutesReceiver) {
+ mux.HandleFunc("GET /", func(response http.ResponseWriter, request *http.Request) {
+ request.ParseForm()
+ var form In
+ form.field = request.FormValue("field")
+ data := receiver.F(form)
+ execute(response, request, true, "GET / F(form)", http.StatusOK, data)
+ })
+}
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
+ buf := bytes.NewBuffer(nil)
+ if err := templates.ExecuteTemplate(buf, name, data); err != nil {
+ http.Error(response, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ if writeHeader {
+ response.WriteHeader(code)
+ }
+ _, _ = buf.WriteTo(response)
+}
+`,
+ },
+ {
+ Name: "F is defined and form field has an input tag",
+ Templates: `{{define "GET / F(form)"}}Hello, {{.}}!{{end}}`,
+ ReceiverPackage: `
+-- in.go --
+package main
+
+type (
+ T struct{}
+ In struct{
+ field string ` + "`input:\"some-field\"`" + `
+ }
+)
+
+func (T) F(form In) int { return 0 }
+`,
+ Receiver: "T",
+ ExpectedFile: `package main
+
+import (
+ "net/http"
+ "bytes"
+)
+
+type RoutesReceiver interface {
+ F(form In) int
+}
+
+func routes(mux *http.ServeMux, receiver RoutesReceiver) {
+ mux.HandleFunc("GET /", func(response http.ResponseWriter, request *http.Request) {
+ request.ParseForm()
+ var form In
+ form.field = request.FormValue("some-field")
+ data := receiver.F(form)
+ execute(response, request, true, "GET / F(form)", http.StatusOK, data)
+ })
+}
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
+ buf := bytes.NewBuffer(nil)
+ if err := templates.ExecuteTemplate(buf, name, data); err != nil {
+ http.Error(response, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ if writeHeader {
+ response.WriteHeader(code)
+ }
+ _, _ = buf.WriteTo(response)
+}
`,
},
} {
From d0039139e93885e398a919562d2d0690337abec0 Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Wed, 28 Aug 2024 18:15:23 -0700
Subject: [PATCH 17/26] refactor: add helper function to get field name
---
generate.go | 25 +++++++++++++------------
1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/generate.go b/generate.go
index 107e4a0..214bbff 100644
--- a/generate.go
+++ b/generate.go
@@ -181,18 +181,7 @@ func (def TemplateName) funcLit(method *ast.FuncType, files []*ast.File) (*ast.F
formDeclaration(arg.Name, tp))
if formStruct != nil {
for _, field := range formStruct.Fields.List {
- var inputNameTag string
- if field.Tag != nil {
- v, _ := strconv.Unquote(field.Tag.Value)
- tags := reflect.StructTag(v)
- n, hasInputTag := tags.Lookup("input")
- if hasInputTag {
- inputNameTag = n
- }
- }
-
for _, name := range field.Names {
- inputName := cmp.Or(inputNameTag, name.Name)
lit.Body.List = append(lit.Body.List, &ast.AssignStmt{
Tok: token.ASSIGN,
Lhs: []ast.Expr{
@@ -210,7 +199,7 @@ func (def TemplateName) funcLit(method *ast.FuncType, files []*ast.File) (*ast.F
Args: []ast.Expr{
&ast.BasicLit{
Kind: token.STRING,
- Value: strconv.Quote(inputName),
+ Value: strconv.Quote(formInputName(field, name)),
},
},
},
@@ -294,6 +283,18 @@ func (def TemplateName) funcLit(method *ast.FuncType, files []*ast.File) (*ast.F
return lit, imports, nil
}
+func formInputName(field *ast.Field, name *ast.Ident) string {
+ if field.Tag != nil {
+ v, _ := strconv.Unquote(field.Tag.Value)
+ tags := reflect.StructTag(v)
+ n, hasInputTag := tags.Lookup("input")
+ if hasInputTag {
+ return n
+ }
+ }
+ return name.Name
+}
+
func (def TemplateName) funcType() (*ast.FuncType, []*ast.ImportSpec) {
method := &ast.FuncType{
Params: &ast.FieldList{},
From 8ead025d1811a0b9976dc413e0ac25524591d597 Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Wed, 28 Aug 2024 18:48:04 -0700
Subject: [PATCH 18/26] add test for tag parsing
---
generate_test.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/generate_test.go b/generate_test.go
index 07f4b28..6892c1e 100644
--- a/generate_test.go
+++ b/generate_test.go
@@ -730,6 +730,56 @@ type RoutesReceiver interface {
F(form In) int
}
+func routes(mux *http.ServeMux, receiver RoutesReceiver) {
+ mux.HandleFunc("GET /", func(response http.ResponseWriter, request *http.Request) {
+ request.ParseForm()
+ var form In
+ form.field = request.FormValue("some-field")
+ data := receiver.F(form)
+ execute(response, request, true, "GET / F(form)", http.StatusOK, data)
+ })
+}
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
+ buf := bytes.NewBuffer(nil)
+ if err := templates.ExecuteTemplate(buf, name, data); err != nil {
+ http.Error(response, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ if writeHeader {
+ response.WriteHeader(code)
+ }
+ _, _ = buf.WriteTo(response)
+}
+`,
+ },
+ {
+ Name: "F is defined and form field has an input tag",
+ Templates: `{{define "GET / F(form)"}}Hello, {{.}}!{{end}}`,
+ ReceiverPackage: `
+-- in.go --
+package main
+
+type (
+ T struct{}
+ In struct{
+ field string ` + "`input:\"some-field\"`" + `
+ }
+)
+
+func (T) F(form In) int { return 0 }
+`,
+ Receiver: "T",
+ ExpectedFile: `package main
+
+import (
+ "net/http"
+ "bytes"
+)
+
+type RoutesReceiver interface {
+ F(form In) int
+}
+
func routes(mux *http.ServeMux, receiver RoutesReceiver) {
mux.HandleFunc("GET /", func(response http.ResponseWriter, request *http.Request) {
request.ParseForm()
From 1802f2b96505db04ed9c8ff6b0b6ffb97e586789 Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Wed, 28 Aug 2024 19:01:08 -0700
Subject: [PATCH 19/26] parse forms fields with non-string basic types
---
generate.go | 53 ++++++++------
generate_test.go | 181 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 214 insertions(+), 20 deletions(-)
diff --git a/generate.go b/generate.go
index 214bbff..cafc936 100644
--- a/generate.go
+++ b/generate.go
@@ -151,6 +151,7 @@ func (def TemplateName) funcLit(method *ast.FuncType, files []*ast.File) (*ast.F
}
}
}
+ const errVarIdent = "err"
var (
imports []*ast.ImportSpec
writeHeader = true
@@ -182,29 +183,42 @@ func (def TemplateName) funcLit(method *ast.FuncType, files []*ast.File) (*ast.F
if formStruct != nil {
for _, field := range formStruct.Fields.List {
for _, name := range field.Names {
- lit.Body.List = append(lit.Body.List, &ast.AssignStmt{
- Tok: token.ASSIGN,
- Lhs: []ast.Expr{
- &ast.SelectorExpr{
- X: ast.NewIdent(arg.Name),
- Sel: ast.NewIdent(name.Name),
- },
+ fieldExpr := &ast.SelectorExpr{
+ X: ast.NewIdent(arg.Name),
+ Sel: ast.NewIdent(name.Name),
+ }
+ errCheck := source.ErrorCheckReturn(errVarIdent, &ast.ExprStmt{X: &ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent(httpPackageIdent),
+ Sel: ast.NewIdent("Error"),
},
- Rhs: []ast.Expr{
+ Args: []ast.Expr{
+ ast.NewIdent(httpResponseField().Names[0].Name),
&ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
- Sel: ast.NewIdent("FormValue"),
- },
- Args: []ast.Expr{
- &ast.BasicLit{
- Kind: token.STRING,
- Value: strconv.Quote(formInputName(field, name)),
- },
- },
+ Fun: &ast.SelectorExpr{X: ast.NewIdent("err"), Sel: ast.NewIdent("Error")},
+ Args: []ast.Expr{},
+ },
+ source.HTTPStatusCode(httpPackageIdent, http.StatusBadRequest),
+ },
+ }}, &ast.ReturnStmt{})
+
+ statements, parseImports, err := parseStringStatements(name.Name, fieldExpr, errVarIdent, &ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
+ Sel: ast.NewIdent("FormValue"),
+ },
+ Args: []ast.Expr{
+ &ast.BasicLit{
+ Kind: token.STRING,
+ Value: strconv.Quote(formInputName(field, name)),
},
},
- })
+ }, field.Type, token.ASSIGN, errCheck)
+ if err != nil {
+ return nil, nil, err
+ }
+ lit.Body.List = append(lit.Body.List, statements...)
+ imports = append(imports, parseImports...)
}
}
} else {
@@ -212,7 +226,6 @@ func (def TemplateName) funcLit(method *ast.FuncType, files []*ast.File) (*ast.F
}
call.Args = append(call.Args, ast.NewIdent(arg.Name))
default:
- const errVarIdent = "err"
errCheck := source.ErrorCheckReturn(errVarIdent, &ast.ExprStmt{X: &ast.CallExpr{
Fun: &ast.SelectorExpr{
X: ast.NewIdent(httpPackageIdent),
diff --git a/generate_test.go b/generate_test.go
index 6892c1e..f52f25e 100644
--- a/generate_test.go
+++ b/generate_test.go
@@ -800,6 +800,187 @@ func execute(response http.ResponseWriter, request *http.Request, writeHeader bo
}
_, _ = buf.WriteTo(response)
}
+`,
+ },
+ {
+ Name: "F is defined and form has two string fields",
+ Templates: `{{define "GET / F(form)"}}Hello, {{.}}!{{end}}`,
+ ReceiverPackage: `
+-- in.go --
+package main
+
+type (
+ T struct{}
+ In struct{
+ fieldInt int
+ fieldInt64 int64
+ fieldInt32 int32
+ fieldInt16 int16
+ fieldInt8 int8
+ fieldUint uint
+ fieldUint64 uint64
+ fieldUint16 uint16
+ fieldUint32 uint32
+ fieldUint16 uint16
+ fieldUint8 uint8
+ fieldBool bool
+ }
+)
+
+func (T) F(form In) int { return 0 }
+`,
+ Receiver: "T",
+ ExpectedFile: `package main
+
+import (
+ "net/http"
+ "strconv"
+ "bytes"
+)
+
+type RoutesReceiver interface {
+ F(form In) int
+}
+
+func routes(mux *http.ServeMux, receiver RoutesReceiver) {
+ mux.HandleFunc("GET /", func(response http.ResponseWriter, request *http.Request) {
+ request.ParseForm()
+ var form In
+ fieldIntParsed, err := strconv.Atoi(request.FormValue("fieldInt"))
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldInt = int(fieldIntParsed)
+ form.fieldInt64, err := strconv.ParseInt(request.FormValue("fieldInt64"), 10, 64)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ fieldInt32Parsed, err := strconv.ParseInt(request.FormValue("fieldInt32"), 10, 32)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldInt32 = int32(fieldInt32Parsed)
+ fieldInt16Parsed, err := strconv.ParseInt(request.FormValue("fieldInt16"), 10, 16)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldInt16 = int16(fieldInt16Parsed)
+ fieldInt8Parsed, err := strconv.ParseInt(request.FormValue("fieldInt8"), 10, 8)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldInt8 = int8(fieldInt8Parsed)
+ fieldUintParsed, err := strconv.ParseUint(request.FormValue("fieldUint"), 10, 64)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldUint = uint(fieldUintParsed)
+ form.fieldUint64, err := strconv.ParseUint(request.FormValue("fieldUint64"), 10, 64)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ fieldUint16Parsed, err := strconv.ParseUint(request.FormValue("fieldUint16"), 10, 16)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldUint16 = uint16(fieldUint16Parsed)
+ fieldUint32Parsed, err := strconv.ParseUint(request.FormValue("fieldUint32"), 10, 32)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldUint32 = uint32(fieldUint32Parsed)
+ fieldUint16Parsed, err := strconv.ParseUint(request.FormValue("fieldUint16"), 10, 16)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldUint16 = uint16(fieldUint16Parsed)
+ fieldUint8Parsed, err := strconv.ParseUint(request.FormValue("fieldUint8"), 10, 8)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldUint8 = uint8(fieldUint8Parsed)
+ form.fieldBool, err := strconv.ParseBool(request.FormValue("fieldBool"))
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ data := receiver.F(form)
+ execute(response, request, true, "GET / F(form)", http.StatusOK, data)
+ })
+}
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
+ buf := bytes.NewBuffer(nil)
+ if err := templates.ExecuteTemplate(buf, name, data); err != nil {
+ http.Error(response, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ if writeHeader {
+ response.WriteHeader(code)
+ }
+ _, _ = buf.WriteTo(response)
+}
+`,
+ },
+ {
+ Name: "F is defined and form has two string fields",
+ Templates: `{{define "GET / F(form)"}}Hello, {{.}}!{{end}}`,
+ ReceiverPackage: `
+-- in.go --
+package main
+
+type (
+ T struct{}
+ In struct{
+ field1, field2 string
+ }
+)
+
+func (T) F(form In) int { return 0 }
+`,
+ Receiver: "T",
+ ExpectedFile: `package main
+
+import (
+ "net/http"
+ "bytes"
+)
+
+type RoutesReceiver interface {
+ F(form In) int
+}
+
+func routes(mux *http.ServeMux, receiver RoutesReceiver) {
+ mux.HandleFunc("GET /", func(response http.ResponseWriter, request *http.Request) {
+ request.ParseForm()
+ var form In
+ form.field1 = request.FormValue("field1")
+ form.field2 = request.FormValue("field2")
+ data := receiver.F(form)
+ execute(response, request, true, "GET / F(form)", http.StatusOK, data)
+ })
+}
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
+ buf := bytes.NewBuffer(nil)
+ if err := templates.ExecuteTemplate(buf, name, data); err != nil {
+ http.Error(response, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ if writeHeader {
+ response.WriteHeader(code)
+ }
+ _, _ = buf.WriteTo(response)
+}
`,
},
} {
From 3b7a4909822d8f8c88d0143b6444d4fb2933fcdb Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Thu, 29 Aug 2024 00:01:43 -0700
Subject: [PATCH 20/26] add error message for unsupported type
---
generate.go | 2 +-
generate_test.go | 19 +++++++++++++++++++
2 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/generate.go b/generate.go
index cafc936..8980da2 100644
--- a/generate.go
+++ b/generate.go
@@ -215,7 +215,7 @@ func (def TemplateName) funcLit(method *ast.FuncType, files []*ast.File) (*ast.F
},
}, field.Type, token.ASSIGN, errCheck)
if err != nil {
- return nil, nil, err
+ return nil, nil, fmt.Errorf("failed to generate parse statements for form field %s: %w", name.Name, err)
}
lit.Body.List = append(lit.Body.List, statements...)
imports = append(imports, parseImports...)
diff --git a/generate_test.go b/generate_test.go
index f52f25e..8dbeabb 100644
--- a/generate_test.go
+++ b/generate_test.go
@@ -983,6 +983,25 @@ func execute(response http.ResponseWriter, request *http.Request, writeHeader bo
}
`,
},
+ {
+ Name: "F is defined and form has unsupported field type",
+ Templates: `{{define "GET / F(form)"}}Hello, {{.}}!{{end}}`,
+ ReceiverPackage: `
+-- in.go --
+package main
+
+type (
+ T struct{}
+ In struct{
+ ts time.Time
+ }
+)
+
+func (T) F(form In) int { return 0 }
+`,
+ Receiver: "T",
+ ExpectedError: "failed to generate parse statements for form field ts: unsupported type: time.Time",
+ },
} {
t.Run(tt.Name, func(t *testing.T) {
ts := template.Must(template.New(tt.Name).Parse(tt.Templates))
From 87c0586e223ae8247fc86c18af720c66ec6dad6d Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Thu, 29 Aug 2024 08:52:50 -0700
Subject: [PATCH 21/26] consolidate happy path tests into higher level
---
generate_internal_test.go | 104 ------------------
generate_test.go | 216 ++++++++++++++++++++++++++++++++++++++
2 files changed, 216 insertions(+), 104 deletions(-)
diff --git a/generate_internal_test.go b/generate_internal_test.go
index ecf02e1..2c663da 100644
--- a/generate_internal_test.go
+++ b/generate_internal_test.go
@@ -6,112 +6,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
-
- "github.com/crhntr/muxt/internal/source"
)
-func TestTemplateName_funcLit(t *testing.T) {
- for _, tt := range []struct {
- Name string
- In string
- Out string
- Imports []string
- Method *ast.FuncType
- }{
- {
- Name: "get",
- In: "GET /",
- Out: `func(response http.ResponseWriter, request *http.Request) {
- execute(response, request, true, "GET /", http.StatusOK, request)
-}`,
- },
- {
- Name: "call F",
- In: "GET / F()",
- Out: `func(response http.ResponseWriter, request *http.Request) {
- data := receiver.F()
- execute(response, request, true, "GET / F()", http.StatusOK, data)
-}`,
- },
- {
- Name: "call F with argument request",
- In: "GET / F(request)",
- Method: &ast.FuncType{
- Params: &ast.FieldList{List: []*ast.Field{{Type: httpRequestField().Type}}},
- Results: &ast.FieldList{List: []*ast.Field{{Type: ast.NewIdent("any")}}},
- },
- Out: `func(response http.ResponseWriter, request *http.Request) {
- data := receiver.F(request)
- execute(response, request, true, "GET / F(request)", http.StatusOK, data)
-}`,
- },
- {
- Name: "call F with argument response",
- In: "GET / F(response)",
- Method: &ast.FuncType{
- Params: &ast.FieldList{List: []*ast.Field{{Type: httpResponseField().Type, Names: []*ast.Ident{{Name: "res"}}}}},
- Results: &ast.FieldList{List: []*ast.Field{{Type: ast.NewIdent("any")}}},
- },
- Out: `func(response http.ResponseWriter, request *http.Request) {
- data := receiver.F(response)
- execute(response, request, false, "GET / F(response)", http.StatusOK, data)
-}`,
- },
- {
- Name: "call F with argument context",
- In: "GET / F(ctx)",
- Method: &ast.FuncType{
- Params: &ast.FieldList{List: []*ast.Field{{Type: contextContextField().Type, Names: []*ast.Ident{{Name: "reqCtx"}}}}},
- Results: &ast.FieldList{List: []*ast.Field{{Type: ast.NewIdent("any")}}},
- },
- Out: `func(response http.ResponseWriter, request *http.Request) {
- ctx := request.Context()
- data := receiver.F(ctx)
- execute(response, request, true, "GET / F(ctx)", http.StatusOK, data)
-}`,
- },
- {
- Name: "call F with argument path param",
- In: "GET /{param} F(param)",
- Method: &ast.FuncType{
- Params: &ast.FieldList{List: []*ast.Field{{Type: ast.NewIdent("string")}}},
- Results: &ast.FieldList{List: []*ast.Field{{Type: ast.NewIdent("any")}}},
- },
- Out: `func(response http.ResponseWriter, request *http.Request) {
- param := request.PathValue("param")
- data := receiver.F(param)
- execute(response, request, true, "GET /{param} F(param)", http.StatusOK, data)
-}`,
- },
- {
- Name: "call F with multiple arguments",
- In: "GET /{userName} F(ctx, userName)",
- Method: &ast.FuncType{
- Params: &ast.FieldList{List: []*ast.Field{
- {Type: contextContextField().Type, Names: []*ast.Ident{{Name: "ctx"}}},
- {Type: ast.NewIdent("string"), Names: []*ast.Ident{{Name: "n"}}},
- }},
- Results: &ast.FieldList{List: []*ast.Field{{Type: ast.NewIdent("any")}}},
- },
- Out: `func(response http.ResponseWriter, request *http.Request) {
- ctx := request.Context()
- userName := request.PathValue("userName")
- data := receiver.F(ctx, userName)
- execute(response, request, true, "GET /{userName} F(ctx, userName)", http.StatusOK, data)
-}`,
- },
- } {
- t.Run(tt.Name, func(t *testing.T) {
- pat, err, ok := NewTemplateName(tt.In)
- require.True(t, ok)
- require.NoError(t, err)
- out, _, err := pat.funcLit(tt.Method, nil)
- require.NoError(t, err)
- assert.Equal(t, tt.Out, source.Format(out))
- })
- }
-}
-
func TestTemplateName_HandlerFuncLit_err(t *testing.T) {
for _, tt := range []struct {
Name string
diff --git a/generate_test.go b/generate_test.go
index 8dbeabb..613cef4 100644
--- a/generate_test.go
+++ b/generate_test.go
@@ -1002,6 +1002,222 @@ func (T) F(form In) int { return 0 }
Receiver: "T",
ExpectedError: "failed to generate parse statements for form field ts: unsupported type: time.Time",
},
+ {
+ Name: "call F",
+ Templates: `{{define "GET / F()"}}Hello, world!{{end}}`,
+ Receiver: "T",
+ PackageName: "main",
+ ReceiverPackage: `-- in.go --
+package main
+
+type T struct{}
+`,
+ ExpectedFile: `package main
+
+import (
+ "net/http"
+ "bytes"
+)
+
+type RoutesReceiver interface {
+ F() any
+}
+
+func routes(mux *http.ServeMux, receiver RoutesReceiver) {
+ mux.HandleFunc("GET /", func(response http.ResponseWriter, request *http.Request) {
+ data := receiver.F()
+ execute(response, request, true, "GET / F()", http.StatusOK, data)
+ })
+}
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
+ buf := bytes.NewBuffer(nil)
+ if err := templates.ExecuteTemplate(buf, name, data); err != nil {
+ http.Error(response, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ if writeHeader {
+ response.WriteHeader(code)
+ }
+ _, _ = buf.WriteTo(response)
+}
+`,
+ },
+ {
+ Name: "no handler",
+ Templates: `{{define "GET /"}}Hello, world!{{end}}`,
+ Receiver: "T",
+ PackageName: "main",
+ ReceiverPackage: `-- in.go --
+package main
+
+type T struct{}
+
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {}
+`,
+ ExpectedFile: `package main
+
+import "net/http"
+
+type RoutesReceiver interface {
+}
+
+func routes(mux *http.ServeMux, receiver RoutesReceiver) {
+ mux.HandleFunc("GET /", func(response http.ResponseWriter, request *http.Request) {
+ execute(response, request, true, "GET /", http.StatusOK, request)
+ })
+}
+`,
+ },
+ {
+ Name: "no handler",
+ Templates: `{{define "GET /"}}Hello, world!{{end}}`,
+ Receiver: "T",
+ PackageName: "main",
+ ReceiverPackage: `-- in.go --
+package main
+
+type T struct{}
+
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {}
+`,
+ ExpectedFile: `package main
+
+import "net/http"
+
+type RoutesReceiver interface {
+}
+
+func routes(mux *http.ServeMux, receiver RoutesReceiver) {
+ mux.HandleFunc("GET /", func(response http.ResponseWriter, request *http.Request) {
+ execute(response, request, true, "GET /", http.StatusOK, request)
+ })
+}
+`,
+ },
+ {
+ Name: "call F with argument response",
+ Templates: `{{define "GET / F(response)"}}{{end}}`,
+ ReceiverPackage: `-- in.go --
+package main
+
+type T struct{}
+
+func (T) F(http.ResponseWriter) any {return nil}
+
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {}
+`,
+ ExpectedFile: `package main
+
+import "net/http"
+
+type RoutesReceiver interface {
+ F(response http.ResponseWriter) any
+}
+
+func routes(mux *http.ServeMux, receiver RoutesReceiver) {
+ mux.HandleFunc("GET /", func(response http.ResponseWriter, request *http.Request) {
+ data := receiver.F(response)
+ execute(response, request, false, "GET / F(response)", http.StatusOK, data)
+ })
+}
+`,
+ },
+ {
+ Name: "call F with argument context",
+ Templates: `{{define "GET / F(ctx)"}}{{end}}`,
+ ReceiverPackage: `-- in.go --
+package main
+
+type T struct{}
+
+func (T) F(ctx context.Context) any {return nil}
+
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {}
+`,
+ ExpectedFile: `package main
+
+import (
+ "context"
+ "net/http"
+)
+
+type RoutesReceiver interface {
+ F(ctx context.Context) any
+}
+
+func routes(mux *http.ServeMux, receiver RoutesReceiver) {
+ mux.HandleFunc("GET /", func(response http.ResponseWriter, request *http.Request) {
+ ctx := request.Context()
+ data := receiver.F(ctx)
+ execute(response, request, true, "GET / F(ctx)", http.StatusOK, data)
+ })
+}
+`,
+ },
+ {
+ Name: "call F with argument path param",
+ Templates: `{{define "GET /{param} F(param)"}}{{end}}`,
+ ReceiverPackage: `-- in.go --
+package main
+
+type T struct{}
+
+func (T) F(param string) any {return nil}
+
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {}
+`,
+ ExpectedFile: `package main
+
+import "net/http"
+
+type RoutesReceiver interface {
+ F(param string) any
+}
+
+func routes(mux *http.ServeMux, receiver RoutesReceiver) {
+ mux.HandleFunc("GET /{param}", func(response http.ResponseWriter, request *http.Request) {
+ param := request.PathValue("param")
+ data := receiver.F(param)
+ execute(response, request, true, "GET /{param} F(param)", http.StatusOK, data)
+ })
+}
+`,
+ },
+ {
+ Name: "call F with multiple arguments",
+ Templates: `{{define "GET /{userName} F(ctx, userName)"}}{{end}}`,
+ ReceiverPackage: `-- in.go --
+package main
+
+import "context"
+
+type T struct{}
+
+func (T) F(ctx context.Context, userName string) any {return nil}
+
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {}
+`,
+ ExpectedFile: `package main
+
+import (
+ "context"
+ "net/http"
+)
+
+type RoutesReceiver interface {
+ F(ctx context.Context, userName string) any
+}
+
+func routes(mux *http.ServeMux, receiver RoutesReceiver) {
+ mux.HandleFunc("GET /{userName}", func(response http.ResponseWriter, request *http.Request) {
+ ctx := request.Context()
+ userName := request.PathValue("userName")
+ data := receiver.F(ctx, userName)
+ execute(response, request, true, "GET /{userName} F(ctx, userName)", http.StatusOK, data)
+ })
+}
+`,
+ },
} {
t.Run(tt.Name, func(t *testing.T) {
ts := template.Must(template.New(tt.Name).Parse(tt.Templates))
From 2015f94b8ba48f7eda3528a178f0c4e038ad8c55 Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Thu, 29 Aug 2024 09:42:39 -0700
Subject: [PATCH 22/26] test: consolidate error tests
---
generate_internal_test.go | 114 -----------------------------
generate_test.go | 146 +++++++++++++++++++++++++++++++++++++-
2 files changed, 145 insertions(+), 115 deletions(-)
delete mode 100644 generate_internal_test.go
diff --git a/generate_internal_test.go b/generate_internal_test.go
deleted file mode 100644
index 2c663da..0000000
--- a/generate_internal_test.go
+++ /dev/null
@@ -1,114 +0,0 @@
-package muxt
-
-import (
- "go/ast"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestTemplateName_HandlerFuncLit_err(t *testing.T) {
- for _, tt := range []struct {
- Name string
- In string
- ErrSub string
- Method *ast.FuncType
- }{
- {
- Name: "missing arguments",
- In: "GET / F()",
- Method: &ast.FuncType{
- Params: &ast.FieldList{List: []*ast.Field{{Type: ast.NewIdent("string")}}},
- Results: &ast.FieldList{List: []*ast.Field{{Type: ast.NewIdent("any")}}},
- },
- ErrSub: "handler func F(string) any expects 1 arguments but call F() has 0",
- },
- {
- Name: "extra arguments",
- In: "GET /{name} F(ctx, name)",
- Method: &ast.FuncType{
- Params: &ast.FieldList{List: []*ast.Field{
- {Type: &ast.SelectorExpr{X: ast.NewIdent(contextPackageIdent), Sel: ast.NewIdent(contextContextTypeIdent)}},
- }},
- Results: &ast.FieldList{List: []*ast.Field{{Type: ast.NewIdent("any")}}},
- },
- ErrSub: "handler func F(context.Context) any expects 1 arguments but call F(ctx, name) has 2",
- },
- {
- Name: "wrong argument type request",
- In: "GET / F(request)",
- Method: &ast.FuncType{
- Params: &ast.FieldList{List: []*ast.Field{
- {Type: ast.NewIdent("string")},
- }},
- Results: &ast.FieldList{List: []*ast.Field{{Type: ast.NewIdent("any")}}},
- },
- ErrSub: "method expects type string but request is *http.Request",
- },
- {
- Name: "wrong argument type ctx",
- In: "GET / F(ctx)",
- Method: &ast.FuncType{
- Params: &ast.FieldList{List: []*ast.Field{
- {Type: ast.NewIdent("string")},
- }},
- Results: &ast.FieldList{List: []*ast.Field{{Type: ast.NewIdent("any")}}},
- },
- ErrSub: "method expects type string but ctx is context.Context",
- },
- {
- Name: "wrong argument type response",
- In: "GET / F(response)",
- Method: &ast.FuncType{
- Params: &ast.FieldList{List: []*ast.Field{
- {Type: ast.NewIdent("string")},
- }},
- Results: &ast.FieldList{List: []*ast.Field{{Type: ast.NewIdent("any")}}},
- },
- ErrSub: "method expects type string but response is http.ResponseWriter",
- },
- {
- Name: "wrong argument type path value",
- In: "GET /{name} F(name)",
- Method: &ast.FuncType{
- Params: &ast.FieldList{List: []*ast.Field{
- {Type: ast.NewIdent("float64")},
- }},
- Results: &ast.FieldList{List: []*ast.Field{{Type: ast.NewIdent("any")}}},
- },
- ErrSub: "method param type float64 not supported",
- },
- {
- Name: "wrong argument type request ptr",
- In: "GET / F(request)",
- Method: &ast.FuncType{
- Params: &ast.FieldList{List: []*ast.Field{
- {Type: &ast.StarExpr{X: ast.NewIdent("T")}},
- }},
- Results: &ast.FieldList{List: []*ast.Field{{Type: ast.NewIdent("any")}}},
- },
- ErrSub: "method expects type *T but request is *http.Request",
- },
- {
- Name: "wrong argument type in field list",
- In: "GET /post/{postID}/comment/{commentID} F(ctx, request, commentID)",
- Method: &ast.FuncType{
- Params: &ast.FieldList{List: []*ast.Field{
- {Type: contextContextField().Type, Names: []*ast.Ident{{Name: "ctx"}}},
- {Names: []*ast.Ident{ast.NewIdent("postID"), ast.NewIdent("commentID")}, Type: ast.NewIdent("string")},
- }},
- Results: &ast.FieldList{List: []*ast.Field{{Type: ast.NewIdent("any")}}},
- },
- ErrSub: "method expects type string but request is *http.Request",
- },
- } {
- t.Run(tt.Name, func(t *testing.T) {
- pat, err, ok := NewTemplateName(tt.In)
- require.True(t, ok)
- require.NoError(t, err)
- _, _, err = pat.funcLit(tt.Method, nil)
- assert.ErrorContains(t, err, tt.ErrSub)
- })
- }
-}
diff --git a/generate_test.go b/generate_test.go
index 613cef4..3f66cfb 100644
--- a/generate_test.go
+++ b/generate_test.go
@@ -26,7 +26,6 @@ func TestGenerate(t *testing.T) {
TemplatesVar string
RoutesFunc string
Imports []string
- Method *ast.FuncType
ExpectedError string
ExpectedFile string
@@ -1186,6 +1185,7 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
{
Name: "call F with multiple arguments",
Templates: `{{define "GET /{userName} F(ctx, userName)"}}{{end}}`,
+ Receiver: "T",
ReceiverPackage: `-- in.go --
package main
@@ -1218,6 +1218,150 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
}
`,
},
+ {
+ Name: "missing arguments",
+ Templates: `{{define "GET / F()"}}{{end}}`,
+ Receiver: "T",
+ ReceiverPackage: `-- in.go --
+package main
+
+type T struct{}
+
+func (T) F(string) any {return nil}
+
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {}
+`,
+
+ ExpectedError: "handler func F(string) any expects 1 arguments but call F() has 0",
+ },
+ {
+ Name: "extra arguments",
+ Templates: `{{define "GET /{name} F(ctx, name)"}}{{end}}`,
+ Receiver: "T",
+ ReceiverPackage: `-- in.go --
+package main
+
+import (
+ "context"
+ "net/html"
+)
+
+type T struct{}
+
+func (T) F(context.Context) any {return nil}
+
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {}
+`,
+ ExpectedError: "handler func F(context.Context) any expects 1 arguments but call F(ctx, name) has 2",
+ },
+ {
+ Name: "wrong argument type request",
+ Templates: `{{define "GET / F(request)"}}{{end}}`,
+ Receiver: "T",
+ ReceiverPackage: `-- in.go --
+package main
+
+import (
+ "context"
+ "net/html"
+)
+
+type T struct{}
+
+func (T) F(string) any {return nil}
+
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {}
+`,
+ ExpectedError: "method expects type string but request is *http.Request",
+ },
+ {
+ Name: "wrong argument type ctx",
+ Templates: `{{define "GET / F(ctx)"}}{{end}}`,
+ Receiver: "T",
+ ReceiverPackage: `-- in.go --
+package main
+
+import "net/html"
+
+type T struct{}
+
+func (T) F(string) any {return nil}
+
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {}
+`,
+ ExpectedError: "method expects type string but ctx is context.Context",
+ },
+ {
+ Name: "wrong argument type response",
+ Templates: `{{define "GET / F(response)"}}{{end}}`,
+ Receiver: "T",
+ ReceiverPackage: `-- in.go --
+package main
+
+import "net/html"
+
+type T struct{}
+
+func (T) F(string) any {return nil}
+
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {}
+`,
+ ExpectedError: "method expects type string but response is http.ResponseWriter",
+ },
+ {
+ Name: "wrong argument type path value",
+ Templates: `{{define "GET /{name} F(name)"}}{{end}}`,
+ Receiver: "T",
+ ReceiverPackage: `-- in.go --
+package main
+
+import "net/html"
+
+type T struct{}
+
+func (T) F(float64) any {return nil}
+
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {}
+`,
+ ExpectedError: "method param type float64 not supported",
+ },
+ {
+ Name: "wrong argument type request ptr",
+ Templates: `{{define "GET / F(request)"}}{{end}}`,
+ Receiver: "T",
+ ReceiverPackage: `-- in.go --
+package main
+
+import "net/html"
+
+type T struct{}
+
+func (T) F(*T) any {return nil}
+
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {}
+`,
+ ExpectedError: "method expects type *T but request is *http.Request",
+ },
+ {
+ Name: "wrong argument type in field list",
+ Templates: `{{define "GET /post/{postID}/comment/{commentID} F(ctx, request, commentID)"}}{{end}}`,
+ Receiver: "T",
+ ReceiverPackage: `-- in.go --
+package main
+
+import (
+ "context"
+ "net/html"
+)
+
+type T struct{}
+
+func (T) F(context.Context, string, string) any {return nil}
+
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {}
+`,
+ ExpectedError: "method expects type string but request is *http.Request",
+ },
} {
t.Run(tt.Name, func(t *testing.T) {
ts := template.Must(template.New(tt.Name).Parse(tt.Templates))
From 660b89fbb4b492f878b92133dbb57fec7a1af551 Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Thu, 29 Aug 2024 11:07:52 -0700
Subject: [PATCH 23/26] refactor: method nil checks
---
generate.go | 22 ++++++++++------------
1 file changed, 10 insertions(+), 12 deletions(-)
diff --git a/generate.go b/generate.go
index 8980da2..cf6aeda 100644
--- a/generate.go
+++ b/generate.go
@@ -129,7 +129,7 @@ func (def TemplateName) callHandleFunc(handlerFuncLit *ast.FuncLit) *ast.ExprStm
}
func (def TemplateName) funcLit(method *ast.FuncType, files []*ast.File) (*ast.FuncLit, []*ast.ImportSpec, error) {
- if def.handler == "" {
+ if method == nil {
return def.httpRequestReceiverTemplateHandlerFunc(), nil, nil
}
lit := &ast.FuncLit{
@@ -137,18 +137,16 @@ func (def TemplateName) funcLit(method *ast.FuncType, files []*ast.File) (*ast.F
Body: &ast.BlockStmt{},
}
call := &ast.CallExpr{Fun: callReceiverMethod(def.fun)}
+ if method.Params.NumFields() != len(def.call.Args) {
+ return nil, nil, errWrongNumberOfArguments(def, method)
+ }
var formStruct *ast.StructType
- if method != nil {
- if method.Params.NumFields() != len(def.call.Args) {
- return nil, nil, errWrongNumberOfArguments(def, method)
+ for pi, pt := range fieldListTypes(method.Params) {
+ if err := checkArgument(method, pi, def.call.Args[pi], pt, files); err != nil {
+ return nil, nil, err
}
- for pi, pt := range fieldListTypes(method.Params) {
- if err := checkArgument(method, pi, def.call.Args[pi], pt, files); err != nil {
- return nil, nil, err
- }
- if s, ok := findFormStruct(pt, files); ok {
- formStruct = s
- }
+ if s, ok := findFormStruct(pt, files); ok {
+ formStruct = s
}
}
const errVarIdent = "err"
@@ -258,7 +256,7 @@ func (def TemplateName) funcLit(method *ast.FuncType, files []*ast.File) (*ast.F
}
const dataVarIdent = "data"
- if method != nil && len(method.Results.List) > 1 {
+ if len(method.Results.List) > 1 {
errVar := ast.NewIdent("err")
lit.Body.List = append(lit.Body.List,
From db2e1f670fc75f8104cb9aae5d6e1672dbd749c5 Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Thu, 29 Aug 2024 14:57:48 -0700
Subject: [PATCH 24/26] test: refactor constants to use use executeGo
reduces test boilerplate
also removed a duplicate tests
---
generate_test.go | 259 +++++++++--------------------------------------
1 file changed, 46 insertions(+), 213 deletions(-)
diff --git a/generate_test.go b/generate_test.go
index 3f66cfb..c1d9f98 100644
--- a/generate_test.go
+++ b/generate_test.go
@@ -184,14 +184,12 @@ package main
type T struct{}
func (*T) F(username string) int { return 30 }
-`,
+
+` + executeGo,
Receiver: "T",
ExpectedFile: `package main
-import (
- "net/http"
- "bytes"
-)
+import "net/http"
type RoutesReceiver interface {
F(username string) int
@@ -204,17 +202,6 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
execute(response, request, true, "GET /age/{username} F(username)", http.StatusOK, data)
})
}
-func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
- buf := bytes.NewBuffer(nil)
- if err := templates.ExecuteTemplate(buf, name, data); err != nil {
- http.Error(response, err.Error(), http.StatusInternalServerError)
- return
- }
- if writeHeader {
- response.WriteHeader(code)
- }
- _, _ = buf.WriteTo(response)
-}
`,
},
{
@@ -258,17 +245,17 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
-- receiver.go --
package main
+import "net/http"
+
type T struct{}
func (T) F(username string) (int, error) { return 30, error }
-`,
+
+` + executeGo,
Receiver: "T",
ExpectedFile: `package main
-import (
- "net/http"
- "bytes"
-)
+import "net/http"
type RoutesReceiver interface {
F(username string) (int, error)
@@ -285,17 +272,6 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
execute(response, request, true, "GET /age/{username} F(username)", http.StatusOK, data)
})
}
-func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
- buf := bytes.NewBuffer(nil)
- if err := templates.ExecuteTemplate(buf, name, data); err != nil {
- http.Error(response, err.Error(), http.StatusInternalServerError)
- return
- }
- if writeHeader {
- response.WriteHeader(code)
- }
- _, _ = buf.WriteTo(response)
-}
`,
},
{
@@ -308,7 +284,8 @@ package main
type T struct{}
func (T) F(ctx context.Context) int { return 30 }
-`,
+
+` + executeGo,
Receiver: "T",
ExpectedError: "method expects type context.Context but request is *http.Request",
},
@@ -337,14 +314,14 @@ func (T0) F(ctx context.Context) int { return 30 }
func (T) F1(ctx context.Context, username string) int { return 30 }
func (T) F(ctx context.Context, username string) int { return 30 }
-`,
+
+` + executeGo,
Receiver: "T",
ExpectedFile: `package main
import (
"context"
"net/http"
- "bytes"
)
type RoutesReceiver interface {
@@ -359,17 +336,6 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
execute(response, request, true, "GET /age/{username} F(ctx, username)", http.StatusOK, data)
})
}
-func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
- buf := bytes.NewBuffer(nil)
- if err := templates.ExecuteTemplate(buf, name, data); err != nil {
- http.Error(response, err.Error(), http.StatusInternalServerError)
- return
- }
- if writeHeader {
- response.WriteHeader(code)
- }
- _, _ = buf.WriteTo(response)
-}
`,
},
{
@@ -417,13 +383,12 @@ func (T) PassUint8(in uint8) uint8 { return in }
func (T) PassBool(in bool) bool { return in }
func (T) PassByte(in byte) byte { return in }
func (T) PassRune(in rune) rune { return in }
-`,
+` + executeGo,
ExpectedFile: `package main
import (
"net/http"
"strconv"
- "bytes"
)
type RoutesReceiver interface {
@@ -549,17 +514,6 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
execute(response, request, true, "GET /uint8/{value} PassUint8(value)", http.StatusOK, data)
})
}
-func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
- buf := bytes.NewBuffer(nil)
- if err := templates.ExecuteTemplate(buf, name, data); err != nil {
- http.Error(response, err.Error(), http.StatusInternalServerError)
- return
- }
- if writeHeader {
- response.WriteHeader(code)
- }
- _, _ = buf.WriteTo(response)
-}
`,
},
{
@@ -574,14 +528,12 @@ type T struct{}
type In struct{}
func (T) F(form In) any { return nil }
-`,
+
+` + executeGo,
Receiver: "T",
ExpectedFile: `package main
-import (
- "net/http"
- "bytes"
-)
+import "net/http"
type RoutesReceiver interface {
F(form In) any
@@ -595,17 +547,6 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
execute(response, request, true, "GET / F(form)", http.StatusOK, data)
})
}
-func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
- buf := bytes.NewBuffer(nil)
- if err := templates.ExecuteTemplate(buf, name, data); err != nil {
- http.Error(response, err.Error(), http.StatusInternalServerError)
- return
- }
- if writeHeader {
- response.WriteHeader(code)
- }
- _, _ = buf.WriteTo(response)
-}
`,
},
{
@@ -616,14 +557,14 @@ func execute(response http.ResponseWriter, request *http.Request, writeHeader bo
package main
type T struct{}
-`,
+
+` + executeGo,
Receiver: "T",
ExpectedFile: `package main
import (
"net/http"
"net/url"
- "bytes"
)
type RoutesReceiver interface {
@@ -638,17 +579,6 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
execute(response, request, true, "GET / F(form)", http.StatusOK, data)
})
}
-func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
- buf := bytes.NewBuffer(nil)
- if err := templates.ExecuteTemplate(buf, name, data); err != nil {
- http.Error(response, err.Error(), http.StatusInternalServerError)
- return
- }
- if writeHeader {
- response.WriteHeader(code)
- }
- _, _ = buf.WriteTo(response)
-}
`,
},
{
@@ -666,64 +596,12 @@ type (
)
func (T) F(form In) int { return 0 }
-`,
- Receiver: "T",
- ExpectedFile: `package main
-
-import (
- "net/http"
- "bytes"
-)
-type RoutesReceiver interface {
- F(form In) int
-}
-
-func routes(mux *http.ServeMux, receiver RoutesReceiver) {
- mux.HandleFunc("GET /", func(response http.ResponseWriter, request *http.Request) {
- request.ParseForm()
- var form In
- form.field = request.FormValue("field")
- data := receiver.F(form)
- execute(response, request, true, "GET / F(form)", http.StatusOK, data)
- })
-}
-func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
- buf := bytes.NewBuffer(nil)
- if err := templates.ExecuteTemplate(buf, name, data); err != nil {
- http.Error(response, err.Error(), http.StatusInternalServerError)
- return
- }
- if writeHeader {
- response.WriteHeader(code)
- }
- _, _ = buf.WriteTo(response)
-}
-`,
- },
- {
- Name: "F is defined and form field has an input tag",
- Templates: `{{define "GET / F(form)"}}Hello, {{.}}!{{end}}`,
- ReceiverPackage: `
--- in.go --
-package main
-
-type (
- T struct{}
- In struct{
- field string ` + "`input:\"some-field\"`" + `
- }
-)
-
-func (T) F(form In) int { return 0 }
-`,
+` + executeGo,
Receiver: "T",
ExpectedFile: `package main
-import (
- "net/http"
- "bytes"
-)
+import "net/http"
type RoutesReceiver interface {
F(form In) int
@@ -733,22 +611,11 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
mux.HandleFunc("GET /", func(response http.ResponseWriter, request *http.Request) {
request.ParseForm()
var form In
- form.field = request.FormValue("some-field")
+ form.field = request.FormValue("field")
data := receiver.F(form)
execute(response, request, true, "GET / F(form)", http.StatusOK, data)
})
}
-func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
- buf := bytes.NewBuffer(nil)
- if err := templates.ExecuteTemplate(buf, name, data); err != nil {
- http.Error(response, err.Error(), http.StatusInternalServerError)
- return
- }
- if writeHeader {
- response.WriteHeader(code)
- }
- _, _ = buf.WriteTo(response)
-}
`,
},
{
@@ -766,14 +633,11 @@ type (
)
func (T) F(form In) int { return 0 }
-`,
+` + executeGo,
Receiver: "T",
ExpectedFile: `package main
-import (
- "net/http"
- "bytes"
-)
+import "net/http"
type RoutesReceiver interface {
F(form In) int
@@ -788,17 +652,6 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
execute(response, request, true, "GET / F(form)", http.StatusOK, data)
})
}
-func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
- buf := bytes.NewBuffer(nil)
- if err := templates.ExecuteTemplate(buf, name, data); err != nil {
- http.Error(response, err.Error(), http.StatusInternalServerError)
- return
- }
- if writeHeader {
- response.WriteHeader(code)
- }
- _, _ = buf.WriteTo(response)
-}
`,
},
{
@@ -808,6 +661,8 @@ func execute(response http.ResponseWriter, request *http.Request, writeHeader bo
-- in.go --
package main
+import "net/http"
+
type (
T struct{}
In struct{
@@ -827,6 +682,8 @@ type (
)
func (T) F(form In) int { return 0 }
+
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {}
`,
Receiver: "T",
ExpectedFile: `package main
@@ -834,7 +691,6 @@ func (T) F(form In) int { return 0 }
import (
"net/http"
"strconv"
- "bytes"
)
type RoutesReceiver interface {
@@ -918,26 +774,17 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
execute(response, request, true, "GET / F(form)", http.StatusOK, data)
})
}
-func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
- buf := bytes.NewBuffer(nil)
- if err := templates.ExecuteTemplate(buf, name, data); err != nil {
- http.Error(response, err.Error(), http.StatusInternalServerError)
- return
- }
- if writeHeader {
- response.WriteHeader(code)
- }
- _, _ = buf.WriteTo(response)
-}
`,
},
{
- Name: "F is defined and form has two string fields",
+ Name: "F is defined and form has two two names for a single field",
Templates: `{{define "GET / F(form)"}}Hello, {{.}}!{{end}}`,
ReceiverPackage: `
-- in.go --
package main
+import "net/http"
+
type (
T struct{}
In struct{
@@ -946,14 +793,13 @@ type (
)
func (T) F(form In) int { return 0 }
+
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {}
`,
Receiver: "T",
ExpectedFile: `package main
-import (
- "net/http"
- "bytes"
-)
+import "net/http"
type RoutesReceiver interface {
F(form In) int
@@ -969,17 +815,6 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
execute(response, request, true, "GET / F(form)", http.StatusOK, data)
})
}
-func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
- buf := bytes.NewBuffer(nil)
- if err := templates.ExecuteTemplate(buf, name, data); err != nil {
- http.Error(response, err.Error(), http.StatusInternalServerError)
- return
- }
- if writeHeader {
- response.WriteHeader(code)
- }
- _, _ = buf.WriteTo(response)
-}
`,
},
{
@@ -997,6 +832,8 @@ type (
)
func (T) F(form In) int { return 0 }
+
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {}
`,
Receiver: "T",
ExpectedError: "failed to generate parse statements for form field ts: unsupported type: time.Time",
@@ -1010,13 +847,12 @@ func (T) F(form In) int { return 0 }
package main
type T struct{}
+
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {}
`,
ExpectedFile: `package main
-import (
- "net/http"
- "bytes"
-)
+import "net/http"
type RoutesReceiver interface {
F() any
@@ -1028,17 +864,6 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
execute(response, request, true, "GET / F()", http.StatusOK, data)
})
}
-func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {
- buf := bytes.NewBuffer(nil)
- if err := templates.ExecuteTemplate(buf, name, data); err != nil {
- http.Error(response, err.Error(), http.StatusInternalServerError)
- return
- }
- if writeHeader {
- response.WriteHeader(code)
- }
- _, _ = buf.WriteTo(response)
-}
`,
},
{
@@ -1392,3 +1217,11 @@ func methodFuncTypeLoader(t *testing.T, set *token.FileSet, in string) []*ast.Fi
}
return files
}
+
+const executeGo = `-- execute.go --
+package main
+
+import "net/http"
+
+func execute(response http.ResponseWriter, request *http.Request, writeHeader bool, name string, code int, data any) {}
+`
From 8fa6a0c1afecc0669cf6de7d14a77671c5c3ed30 Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Thu, 29 Aug 2024 16:40:06 -0700
Subject: [PATCH 25/26] feat: support field lists
---
generate.go | 222 +++++++++++++++++++++++++++--------------------
generate_test.go | 209 ++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 328 insertions(+), 103 deletions(-)
diff --git a/generate.go b/generate.go
index cf6aeda..f19d9dc 100644
--- a/generate.go
+++ b/generate.go
@@ -200,23 +200,59 @@ func (def TemplateName) funcLit(method *ast.FuncType, files []*ast.File) (*ast.F
},
}}, &ast.ReturnStmt{})
- statements, parseImports, err := parseStringStatements(name.Name, fieldExpr, errVarIdent, &ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
- Sel: ast.NewIdent("FormValue"),
- },
- Args: []ast.Expr{
- &ast.BasicLit{
- Kind: token.STRING,
- Value: strconv.Quote(formInputName(field, name)),
+ assignment := singleAssignment(token.ASSIGN, fieldExpr)
+ if fieldType, ok := field.Type.(*ast.ArrayType); ok {
+ const valVar = "val"
+ assignment = appendAssignment(token.ASSIGN, &ast.SelectorExpr{
+ X: ast.NewIdent(arg.Name),
+ Sel: ast.NewIdent(name.Name),
+ })
+ statements, parseImports, err := parseStringStatements(name.Name, errVarIdent, ast.NewIdent(valVar), fieldType.Elt, errCheck, assignment)
+ if err != nil {
+ return nil, nil, fmt.Errorf("failed to generate parse statements for form field %s: %w", name.Name, err)
+ }
+
+ forLoop := &ast.RangeStmt{
+ Key: ast.NewIdent("_"),
+ Value: ast.NewIdent(valVar),
+ Tok: token.DEFINE,
+ X: &ast.IndexExpr{
+ X: &ast.SelectorExpr{
+ X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
+ Sel: ast.NewIdent("Form"),
+ },
+ Index: &ast.BasicLit{
+ Kind: token.STRING,
+ Value: strconv.Quote(formInputName(field, name)),
+ },
},
- },
- }, field.Type, token.ASSIGN, errCheck)
- if err != nil {
- return nil, nil, fmt.Errorf("failed to generate parse statements for form field %s: %w", name.Name, err)
+ Body: &ast.BlockStmt{
+ List: statements,
+ },
+ }
+
+ lit.Body.List = append(lit.Body.List, forLoop)
+ imports = append(imports, parseImports...)
+ } else {
+ str := &ast.CallExpr{
+ Fun: &ast.SelectorExpr{
+ X: ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest),
+ Sel: ast.NewIdent("FormValue"),
+ },
+ Args: []ast.Expr{
+ &ast.BasicLit{
+ Kind: token.STRING,
+ Value: strconv.Quote(formInputName(field, name)),
+ },
+ },
+ }
+ statements, parseImports, err := parseStringStatements(name.Name, errVarIdent, str, field.Type, errCheck, assignment)
+ if err != nil {
+ return nil, nil, fmt.Errorf("failed to generate parse statements for form field %s: %w", name.Name, err)
+ }
+ lit.Body.List = append(lit.Body.List, statements...)
+ imports = append(imports, parseImports...)
}
- lit.Body.List = append(lit.Body.List, statements...)
- imports = append(imports, parseImports...)
}
}
} else {
@@ -550,12 +586,36 @@ func httpPathValueAssignment(method *ast.FuncType, i int, arg *ast.Ident, errVar
if typeIndex != i {
continue
}
- return parseStringStatements(arg.Name, ast.NewIdent(arg.Name), errVarIdent, str, typeExp, assignTok, errCheck)
+ assignment := singleAssignment(assignTok, ast.NewIdent(arg.Name))
+ return parseStringStatements(arg.Name, errVarIdent, str, typeExp, errCheck, assignment)
}
return nil, nil, fmt.Errorf("type for argumement %d not found", i)
}
-func parseStringStatements(name string, result ast.Expr, errVarIdent string, str, typeExp ast.Expr, assignTok token.Token, errCheck *ast.IfStmt) ([]ast.Stmt, []*ast.ImportSpec, error) {
+func singleAssignment(assignTok token.Token, result ast.Expr) func(exp ast.Expr) ast.Stmt {
+ return func(exp ast.Expr) ast.Stmt {
+ return &ast.AssignStmt{
+ Lhs: []ast.Expr{result},
+ Tok: assignTok,
+ Rhs: []ast.Expr{exp},
+ }
+ }
+}
+
+func appendAssignment(assignTok token.Token, result ast.Expr) func(exp ast.Expr) ast.Stmt {
+ return func(exp ast.Expr) ast.Stmt {
+ return &ast.AssignStmt{
+ Lhs: []ast.Expr{result},
+ Tok: assignTok,
+ Rhs: []ast.Expr{&ast.CallExpr{
+ Fun: ast.NewIdent("append"),
+ Args: []ast.Expr{result, exp},
+ }},
+ }
+ }
+}
+
+func parseStringStatements(name string, errVarIdent string, str, typeExp ast.Expr, errCheck *ast.IfStmt, assignment func(ast.Expr) ast.Stmt) ([]ast.Stmt, []*ast.ImportSpec, error) {
const parsedVarSuffix = "Parsed"
paramTypeIdent, ok := typeExp.(*ast.Ident)
if !ok {
@@ -566,8 +626,10 @@ func parseStringStatements(name string, result ast.Expr, errVarIdent string, str
default:
return nil, nil, fmt.Errorf("method param type %s not supported", source.Format(typeExp))
case "bool":
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{result, ast.NewIdent(errVarIdent)},
+ tmp := name + parsedVarSuffix
+
+ parse := &ast.AssignStmt{
+ Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVarIdent)},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: &ast.SelectorExpr{
@@ -578,7 +640,9 @@ func parseStringStatements(name string, result ast.Expr, errVarIdent string, str
}},
}
- return []ast.Stmt{assign, errCheck}, []*ast.ImportSpec{importSpec("strconv")}, nil
+ assign := assignment(ast.NewIdent(tmp))
+
+ return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "int":
tmp := name + parsedVarSuffix
@@ -594,14 +658,7 @@ func parseStringStatements(name string, result ast.Expr, errVarIdent string, str
}},
}
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{result},
- Tok: assignTok,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: ast.NewIdent(paramTypeIdent.Name),
- Args: []ast.Expr{ast.NewIdent(tmp)},
- }},
- }
+ assign := assignment(ast.NewIdent(tmp))
return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "int16":
@@ -619,14 +676,10 @@ func parseStringStatements(name string, result ast.Expr, errVarIdent string, str
}},
}
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{result},
- Tok: assignTok,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: ast.NewIdent(paramTypeIdent.Name),
- Args: []ast.Expr{ast.NewIdent(tmp)},
- }},
- }
+ assign := assignment(&ast.CallExpr{
+ Fun: ast.NewIdent(paramTypeIdent.Name),
+ Args: []ast.Expr{ast.NewIdent(tmp)},
+ })
return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "int32":
@@ -644,14 +697,10 @@ func parseStringStatements(name string, result ast.Expr, errVarIdent string, str
}},
}
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{result},
- Tok: assignTok,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: ast.NewIdent(paramTypeIdent.Name),
- Args: []ast.Expr{ast.NewIdent(tmp)},
- }},
- }
+ assign := assignment(&ast.CallExpr{
+ Fun: ast.NewIdent(paramTypeIdent.Name),
+ Args: []ast.Expr{ast.NewIdent(tmp)},
+ })
return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "int8":
@@ -669,19 +718,17 @@ func parseStringStatements(name string, result ast.Expr, errVarIdent string, str
}},
}
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{result},
- Tok: assignTok,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: ast.NewIdent(paramTypeIdent.Name),
- Args: []ast.Expr{ast.NewIdent(tmp)},
- }},
- }
+ assign := assignment(&ast.CallExpr{
+ Fun: ast.NewIdent(paramTypeIdent.Name),
+ Args: []ast.Expr{ast.NewIdent(tmp)},
+ })
return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "int64":
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{result, ast.NewIdent(errVarIdent)},
+ tmp := name + parsedVarSuffix
+
+ parse := &ast.AssignStmt{
+ Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVarIdent)},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: &ast.SelectorExpr{
@@ -692,7 +739,9 @@ func parseStringStatements(name string, result ast.Expr, errVarIdent string, str
}},
}
- return []ast.Stmt{assign, errCheck}, []*ast.ImportSpec{importSpec("strconv")}, nil
+ assign := assignment(ast.NewIdent(tmp))
+
+ return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "uint":
tmp := name + parsedVarSuffix
@@ -708,14 +757,10 @@ func parseStringStatements(name string, result ast.Expr, errVarIdent string, str
}},
}
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{result},
- Tok: assignTok,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: ast.NewIdent(paramTypeIdent.Name),
- Args: []ast.Expr{ast.NewIdent(tmp)},
- }},
- }
+ assign := assignment(&ast.CallExpr{
+ Fun: ast.NewIdent(paramTypeIdent.Name),
+ Args: []ast.Expr{ast.NewIdent(tmp)},
+ })
return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "uint16":
@@ -733,14 +778,10 @@ func parseStringStatements(name string, result ast.Expr, errVarIdent string, str
}},
}
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{result},
- Tok: assignTok,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: ast.NewIdent(paramTypeIdent.Name),
- Args: []ast.Expr{ast.NewIdent(tmp)},
- }},
- }
+ assign := assignment(&ast.CallExpr{
+ Fun: ast.NewIdent(paramTypeIdent.Name),
+ Args: []ast.Expr{ast.NewIdent(tmp)},
+ })
return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "uint32":
@@ -758,20 +799,17 @@ func parseStringStatements(name string, result ast.Expr, errVarIdent string, str
}},
}
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{result},
- Tok: assignTok,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: ast.NewIdent(paramTypeIdent.Name),
- Args: []ast.Expr{ast.NewIdent(tmp)},
- }},
- }
+ assign := assignment(&ast.CallExpr{
+ Fun: ast.NewIdent(paramTypeIdent.Name),
+ Args: []ast.Expr{ast.NewIdent(tmp)},
+ })
return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "uint64":
+ tmp := name + parsedVarSuffix
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{result, ast.NewIdent(errVarIdent)},
+ parse := &ast.AssignStmt{
+ Lhs: []ast.Expr{ast.NewIdent(tmp), ast.NewIdent(errVarIdent)},
Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CallExpr{
Fun: &ast.SelectorExpr{
@@ -782,7 +820,9 @@ func parseStringStatements(name string, result ast.Expr, errVarIdent string, str
}},
}
- return []ast.Stmt{assign, errCheck}, []*ast.ImportSpec{importSpec("strconv")}, nil
+ assign := assignment(ast.NewIdent(tmp))
+
+ return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "uint8":
tmp := name + parsedVarSuffix
@@ -798,22 +838,14 @@ func parseStringStatements(name string, result ast.Expr, errVarIdent string, str
}},
}
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{result},
- Tok: assignTok,
- Rhs: []ast.Expr{&ast.CallExpr{
- Fun: ast.NewIdent(paramTypeIdent.Name),
- Args: []ast.Expr{ast.NewIdent(tmp)},
- }},
- }
+ assign := assignment(&ast.CallExpr{
+ Fun: ast.NewIdent(paramTypeIdent.Name),
+ Args: []ast.Expr{ast.NewIdent(tmp)},
+ })
return []ast.Stmt{parse, errCheck, assign}, []*ast.ImportSpec{importSpec("strconv")}, nil
case "string":
- assign := &ast.AssignStmt{
- Lhs: []ast.Expr{result},
- Tok: assignTok,
- Rhs: []ast.Expr{str},
- }
+ assign := assignment(str)
return []ast.Stmt{assign}, nil, nil
}
}
diff --git a/generate_test.go b/generate_test.go
index c1d9f98..de7c021 100644
--- a/generate_test.go
+++ b/generate_test.go
@@ -407,11 +407,12 @@ type RoutesReceiver interface {
func routes(mux *http.ServeMux, receiver RoutesReceiver) {
mux.HandleFunc("GET /bool/{value}", func(response http.ResponseWriter, request *http.Request) {
- value, err := strconv.ParseBool(request.PathValue("value"))
+ valueParsed, err := strconv.ParseBool(request.PathValue("value"))
if err != nil {
http.Error(response, err.Error(), http.StatusBadRequest)
return
}
+ value := valueParsed
data := receiver.PassBool(value)
execute(response, request, true, "GET /bool/{value} PassBool(value)", http.StatusOK, data)
})
@@ -421,7 +422,7 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
http.Error(response, err.Error(), http.StatusBadRequest)
return
}
- value := int(valueParsed)
+ value := valueParsed
data := receiver.PassInt(value)
execute(response, request, true, "GET /int/{value} PassInt(value)", http.StatusOK, data)
})
@@ -446,11 +447,12 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
execute(response, request, true, "GET /int32/{value} PassInt32(value)", http.StatusOK, data)
})
mux.HandleFunc("GET /int64/{value}", func(response http.ResponseWriter, request *http.Request) {
- value, err := strconv.ParseInt(request.PathValue("value"), 10, 64)
+ valueParsed, err := strconv.ParseInt(request.PathValue("value"), 10, 64)
if err != nil {
http.Error(response, err.Error(), http.StatusBadRequest)
return
}
+ value := valueParsed
data := receiver.PassInt64(value)
execute(response, request, true, "GET /int64/{value} PassInt64(value)", http.StatusOK, data)
})
@@ -495,11 +497,12 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
execute(response, request, true, "GET /uint32/{value} PassUint32(value)", http.StatusOK, data)
})
mux.HandleFunc("GET /uint64/{value}", func(response http.ResponseWriter, request *http.Request) {
- value, err := strconv.ParseUint(request.PathValue("value"), 10, 64)
+ valueParsed, err := strconv.ParseUint(request.PathValue("value"), 10, 64)
if err != nil {
http.Error(response, err.Error(), http.StatusBadRequest)
return
}
+ value := valueParsed
data := receiver.PassUint64(value)
execute(response, request, true, "GET /uint64/{value} PassUint64(value)", http.StatusOK, data)
})
@@ -706,12 +709,13 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
http.Error(response, err.Error(), http.StatusBadRequest)
return
}
- form.fieldInt = int(fieldIntParsed)
- form.fieldInt64, err := strconv.ParseInt(request.FormValue("fieldInt64"), 10, 64)
+ form.fieldInt = fieldIntParsed
+ fieldInt64Parsed, err := strconv.ParseInt(request.FormValue("fieldInt64"), 10, 64)
if err != nil {
http.Error(response, err.Error(), http.StatusBadRequest)
return
}
+ form.fieldInt64 = fieldInt64Parsed
fieldInt32Parsed, err := strconv.ParseInt(request.FormValue("fieldInt32"), 10, 32)
if err != nil {
http.Error(response, err.Error(), http.StatusBadRequest)
@@ -736,11 +740,12 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
return
}
form.fieldUint = uint(fieldUintParsed)
- form.fieldUint64, err := strconv.ParseUint(request.FormValue("fieldUint64"), 10, 64)
+ fieldUint64Parsed, err := strconv.ParseUint(request.FormValue("fieldUint64"), 10, 64)
if err != nil {
http.Error(response, err.Error(), http.StatusBadRequest)
return
}
+ form.fieldUint64 = fieldUint64Parsed
fieldUint16Parsed, err := strconv.ParseUint(request.FormValue("fieldUint16"), 10, 16)
if err != nil {
http.Error(response, err.Error(), http.StatusBadRequest)
@@ -765,11 +770,12 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
return
}
form.fieldUint8 = uint8(fieldUint8Parsed)
- form.fieldBool, err := strconv.ParseBool(request.FormValue("fieldBool"))
+ fieldBoolParsed, err := strconv.ParseBool(request.FormValue("fieldBool"))
if err != nil {
http.Error(response, err.Error(), http.StatusBadRequest)
return
}
+ form.fieldBool = fieldBoolParsed
data := receiver.F(form)
execute(response, request, true, "GET / F(form)", http.StatusOK, data)
})
@@ -815,6 +821,193 @@ func routes(mux *http.ServeMux, receiver RoutesReceiver) {
execute(response, request, true, "GET / F(form)", http.StatusOK, data)
})
}
+`,
+ },
+ {
+ Name: "F is defined and form slice field",
+ Templates: `{{define "GET / F(form)"}}Hello, {{.}}!{{end}}`,
+ ReceiverPackage: `
+-- in.go --
+package main
+
+import "net/http"
+
+type (
+ T struct{}
+ In struct{
+ field []string
+ }
+)
+
+func (T) F(form In) int { return 0 }
+
+` + executeGo,
+ Receiver: "T",
+ ExpectedFile: `package main
+
+import "net/http"
+
+type RoutesReceiver interface {
+ F(form In) int
+}
+
+func routes(mux *http.ServeMux, receiver RoutesReceiver) {
+ mux.HandleFunc("GET /", func(response http.ResponseWriter, request *http.Request) {
+ request.ParseForm()
+ var form In
+ for _, val := range request.Form["field"] {
+ form.field = append(form.field, val)
+ }
+ data := receiver.F(form)
+ execute(response, request, true, "GET / F(form)", http.StatusOK, data)
+ })
+}
+`,
+ },
+ {
+ Name: "F is defined and form has typed slice fields",
+ Templates: `{{define "GET / F(form)"}}Hello, {{.}}!{{end}}`,
+ ReceiverPackage: `
+-- in.go --
+package main
+
+type (
+ T struct{}
+ In struct{
+ fieldInt []int
+ fieldInt64 []int64
+ fieldInt32 []int32
+ fieldInt16 []int16
+ fieldInt8 []int8
+ fieldUint []uint
+ fieldUint64 []uint64
+ fieldUint16 []uint16
+ fieldUint32 []uint32
+ fieldUint16 []uint16
+ fieldUint8 []uint8
+ fieldBool []bool
+ }
+)
+
+func (T) F(form In) int { return 0 }
+
+` + executeGo,
+ Receiver: "T",
+ ExpectedFile: `package main
+
+import (
+ "net/http"
+ "strconv"
+)
+
+type RoutesReceiver interface {
+ F(form In) int
+}
+
+func routes(mux *http.ServeMux, receiver RoutesReceiver) {
+ mux.HandleFunc("GET /", func(response http.ResponseWriter, request *http.Request) {
+ request.ParseForm()
+ var form In
+ for _, val := range request.Form["fieldInt"] {
+ fieldIntParsed, err := strconv.Atoi(val)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldInt = append(form.fieldInt, fieldIntParsed)
+ }
+ for _, val := range request.Form["fieldInt64"] {
+ fieldInt64Parsed, err := strconv.ParseInt(val, 10, 64)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldInt64 = append(form.fieldInt64, fieldInt64Parsed)
+ }
+ for _, val := range request.Form["fieldInt32"] {
+ fieldInt32Parsed, err := strconv.ParseInt(val, 10, 32)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldInt32 = append(form.fieldInt32, int32(fieldInt32Parsed))
+ }
+ for _, val := range request.Form["fieldInt16"] {
+ fieldInt16Parsed, err := strconv.ParseInt(val, 10, 16)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldInt16 = append(form.fieldInt16, int16(fieldInt16Parsed))
+ }
+ for _, val := range request.Form["fieldInt8"] {
+ fieldInt8Parsed, err := strconv.ParseInt(val, 10, 8)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldInt8 = append(form.fieldInt8, int8(fieldInt8Parsed))
+ }
+ for _, val := range request.Form["fieldUint"] {
+ fieldUintParsed, err := strconv.ParseUint(val, 10, 64)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldUint = append(form.fieldUint, uint(fieldUintParsed))
+ }
+ for _, val := range request.Form["fieldUint64"] {
+ fieldUint64Parsed, err := strconv.ParseUint(val, 10, 64)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldUint64 = append(form.fieldUint64, fieldUint64Parsed)
+ }
+ for _, val := range request.Form["fieldUint16"] {
+ fieldUint16Parsed, err := strconv.ParseUint(val, 10, 16)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldUint16 = append(form.fieldUint16, uint16(fieldUint16Parsed))
+ }
+ for _, val := range request.Form["fieldUint32"] {
+ fieldUint32Parsed, err := strconv.ParseUint(val, 10, 32)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldUint32 = append(form.fieldUint32, uint32(fieldUint32Parsed))
+ }
+ for _, val := range request.Form["fieldUint16"] {
+ fieldUint16Parsed, err := strconv.ParseUint(val, 10, 16)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldUint16 = append(form.fieldUint16, uint16(fieldUint16Parsed))
+ }
+ for _, val := range request.Form["fieldUint8"] {
+ fieldUint8Parsed, err := strconv.ParseUint(val, 10, 8)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldUint8 = append(form.fieldUint8, uint8(fieldUint8Parsed))
+ }
+ for _, val := range request.Form["fieldBool"] {
+ fieldBoolParsed, err := strconv.ParseBool(val)
+ if err != nil {
+ http.Error(response, err.Error(), http.StatusBadRequest)
+ return
+ }
+ form.fieldBool = append(form.fieldBool, fieldBoolParsed)
+ }
+ data := receiver.F(form)
+ execute(response, request, true, "GET / F(form)", http.StatusOK, data)
+ })
+}
`,
},
{
From 14636ad2e35eef0a140c715f5affdb7d6cc69650 Mon Sep 17 00:00:00 2001
From: Chrstopher Hunter <8398225+crhntr@users.noreply.github.com>
Date: Thu, 29 Aug 2024 20:29:07 -0700
Subject: [PATCH 26/26] add feature level test
---
cmd/muxt/testdata/generate/form.txtar | 93 +++++++++++++++++++++++++++
1 file changed, 93 insertions(+)
create mode 100644 cmd/muxt/testdata/generate/form.txtar
diff --git a/cmd/muxt/testdata/generate/form.txtar b/cmd/muxt/testdata/generate/form.txtar
new file mode 100644
index 0000000..f7200ee
--- /dev/null
+++ b/cmd/muxt/testdata/generate/form.txtar
@@ -0,0 +1,93 @@
+muxt generate --receiver-static-type=T
+
+cat template_routes.go
+
+exec go test -cover
+
+-- template.gohtml --
+{{define "POST / Method(form)" }}{{end}}
+
+-- go.mod --
+module server
+
+go 1.22
+
+-- template.go --
+package server
+
+import (
+ "embed"
+ "html/template"
+)
+
+//go:embed *.gohtml
+var formHTML embed.FS
+
+var templates = template.Must(template.ParseFS(formHTML, "*"))
+
+type Form struct {
+ Count []int `json:"count"`
+ Str string `input:"some-string" json:"str"`
+}
+
+type T struct {
+ spy func(Form) Form
+}
+
+func (t T) Method(form Form) Form {
+ return t.spy(form)
+}
+-- template_test.go --
+package server
+
+import (
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "slices"
+ "strings"
+ "testing"
+)
+
+func Test(t *testing.T) {
+ mux := http.NewServeMux()
+
+ var service T
+
+ service.spy = func(form Form) Form {
+ if exp := []int{7, 14, 21, 29}; !slices.Equal(exp, form.Count) {
+ t.Errorf("exp %v, got %v", exp, form.Count)
+ }
+ if exp := "apple"; form.Str != exp {
+ t.Errorf("exp %v, got %v", exp, form.Str)
+ }
+ return form
+ }
+
+ routes(mux, service)
+
+ req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(url.Values{
+ "some-string": []string{"apple"},
+ "Count": []string{"7", "14", "21", "29"},
+ }.Encode()))
+ req.Header.Set("content-type", "application/x-www-form-urlencoded")
+ rec := httptest.NewRecorder()
+
+ mux.ServeHTTP(rec, req)
+
+ res := rec.Result()
+
+ if res.StatusCode != http.StatusOK {
+ t.Error("expected OK")
+ }
+
+ body, err := io.ReadAll(res.Body)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if exp := ``; string(body) != exp {
+ t.Errorf("exp %v, got %v", exp, string(body))
+ }
+}