Skip to content

Commit

Permalink
feat: override status codes
Browse files Browse the repository at this point in the history
  • Loading branch information
crhntr committed Aug 30, 2024
1 parent 7fa2f64 commit daadcdf
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 44 deletions.
10 changes: 5 additions & 5 deletions cmd/muxt/testdata/generate/form.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ cat template_routes.go
exec go test -cover

-- template.gohtml --
{{define "POST / Method(form)" }}<script>var _ = {{.}}</script>{{end}}
{{define "POST / Method(form) 201" }}<script>var _ = {{.}}</script>{{end}}

-- go.mod --
module server
Expand All @@ -26,8 +26,8 @@ var formHTML embed.FS
var templates = template.Must(template.ParseFS(formHTML, "*"))

type Form struct {
Count []int `json:"count"`
Str string `name:"some-string" json:"str"`
Count []int `json:"count"`
Str string `name:"some-string" json:"str"`
}

type T struct {
Expand Down Expand Up @@ -78,8 +78,8 @@ func Test(t *testing.T) {

res := rec.Result()

if res.StatusCode != http.StatusOK {
t.Error("expected OK")
if res.StatusCode != http.StatusCreated {
t.Error("exp", http.StatusText(http.StatusCreated), "got", http.StatusText(res.StatusCode))
}

body, err := io.ReadAll(res.Body)
Expand Down
8 changes: 4 additions & 4 deletions generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,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 method == nil {
return def.httpRequestReceiverTemplateHandlerFunc(), nil, nil
return def.httpRequestReceiverTemplateHandlerFunc(def.statusCode), nil, nil
}
lit := &ast.FuncLit{
Type: httpHandlerFuncType(),
Expand Down Expand Up @@ -340,7 +340,7 @@ func (def TemplateName) funcLit(method *ast.FuncType, files []*ast.File) (*ast.F
} 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(source.HTTPStatusCode(httpPackageIdent, http.StatusOK), ast.NewIdent(dataVarIdent), writeHeader))
lit.Body.List = append(lit.Body.List, def.executeCall(source.HTTPStatusCode(httpPackageIdent, def.statusCode), ast.NewIdent(dataVarIdent), writeHeader))
return lit, imports, nil
}

Expand Down Expand Up @@ -855,10 +855,10 @@ func (def TemplateName) executeCall(status, data ast.Expr, writeHeader bool) *as
}}
}

func (def TemplateName) httpRequestReceiverTemplateHandlerFunc() *ast.FuncLit {
func (def TemplateName) httpRequestReceiverTemplateHandlerFunc(statusCode int) *ast.FuncLit {
return &ast.FuncLit{
Type: httpHandlerFuncType(),
Body: &ast.BlockStmt{List: []ast.Stmt{def.executeCall(source.HTTPStatusCode(httpPackageIdent, http.StatusOK), ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest), true)}},
Body: &ast.BlockStmt{List: []ast.Stmt{def.executeCall(source.HTTPStatusCode(httpPackageIdent, statusCode), ast.NewIdent(TemplateNameScopeIdentifierHTTPRequest), true)}},
}
}

Expand Down
3 changes: 0 additions & 3 deletions generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1462,9 +1462,6 @@ func execute(response http.ResponseWriter, request *http.Request, writeHeader bo
if tt.ExpectedError == "" {
assert.NoError(t, err)
assert.Equal(t, tt.ExpectedFile, out)
if t.Failed() {
t.Logf(out)
}
} else {
assert.ErrorContains(t, err, tt.ExpectedError)
}
Expand Down
26 changes: 18 additions & 8 deletions name.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"net/http"
"regexp"
"slices"
"strconv"
"strings"

"github.com/crhntr/muxt/internal/source"
Expand Down Expand Up @@ -40,6 +41,7 @@ type TemplateName struct {
name string
method, host, path, endpoint string
handler string
statusCode int

fun *ast.Ident
call *ast.CallExpr
Expand All @@ -57,13 +59,21 @@ func newTemplate(in string) (TemplateName, error, bool) {
}
matches := templateNameMux.FindStringSubmatch(in)
p := TemplateName{
name: in,
method: matches[templateNameMux.SubexpIndex("method")],
host: matches[templateNameMux.SubexpIndex("host")],
path: matches[templateNameMux.SubexpIndex("path")],
handler: strings.TrimSpace(matches[templateNameMux.SubexpIndex("handler")]),
endpoint: matches[templateNameMux.SubexpIndex("endpoint")],
fileSet: token.NewFileSet(),
name: in,
method: matches[templateNameMux.SubexpIndex("method")],
host: matches[templateNameMux.SubexpIndex("host")],
path: matches[templateNameMux.SubexpIndex("path")],
handler: strings.TrimSpace(matches[templateNameMux.SubexpIndex("handler")]),
endpoint: matches[templateNameMux.SubexpIndex("endpoint")],
fileSet: token.NewFileSet(),
statusCode: http.StatusOK,
}
if s := matches[templateNameMux.SubexpIndex("code")]; s != "" {
code, err := strconv.Atoi(strings.TrimSpace(s))
if err != nil {
return TemplateName{}, fmt.Errorf("failed to parse status code: %w", err), true
}
p.statusCode = code
}

switch p.method {
Expand All @@ -86,7 +96,7 @@ func newTemplate(in string) (TemplateName, error, bool) {

var (
pathSegmentPattern = regexp.MustCompile(`/\{([^}]*)}`)
templateNameMux = regexp.MustCompile(`^(?P<endpoint>(((?P<method>[A-Z]+)\s+)?)(?P<host>([^/])*)(?P<path>(/(\S)*)))(?P<handler>.*)$`)
templateNameMux = regexp.MustCompile(`^(?P<endpoint>(((?P<method>[A-Z]+)\s+)?)(?P<host>([^/])*)(?P<path>(/(\S)*)))(?P<handler>\PL+.*\(.*\))?(?P<code>\s\d+)?$`)
)

func (def TemplateName) parsePathValueNames() ([]string, error) {
Expand Down
16 changes: 16 additions & 0 deletions name_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,22 @@ func TestNewTemplateName(t *testing.T) {
assert.ErrorContains(t, err, `forbidden repeated path parameter names: found at least 2 path parameters with name "name"`)
},
},
{
Name: "with status code",
In: "POST / 202",
ExpMatch: true,
TemplateName: func(t *testing.T, pat TemplateName) {
assert.Equal(t, http.StatusAccepted, pat.statusCode)
},
},
{
Name: "with code",
In: "POST /",
ExpMatch: true,
TemplateName: func(t *testing.T, pat TemplateName) {
assert.Equal(t, http.StatusOK, pat.statusCode)
},
},
} {
t.Run(tt.Name, func(t *testing.T) {
pat, err, match := NewTemplateName(tt.In)
Expand Down
54 changes: 30 additions & 24 deletions name_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,46 +25,52 @@ func TestTemplateNames(t *testing.T) {

func TestPattern_parseHandler(t *testing.T) {
for _, tt := range []struct {
Name string
In string
ExpErr string
Name string
In string
ExpErr string
ExpMatch bool
}{
{
Name: "no arg post",
In: "GET / F()",
Name: "no arg post",
In: "GET / F()",
ExpMatch: true,
},
{
Name: "no arg get",
In: "POST / F()",
Name: "no arg get",
In: "POST / F()",
ExpMatch: true,
},
{
Name: "int as handler",
In: "POST / 1",
ExpErr: "expected call, got: 1",
Name: "float64 as handler",
In: "POST / 1.2",
ExpMatch: false,
},
{
Name: "not an expression",
In: "GET / package main",
ExpErr: "failed to parse handler expression: ",
Name: "not an expression",
In: "GET / package main",
ExpMatch: false,
},
{
Name: "function literal",
In: "GET / func() {} ()",
ExpErr: "expected function identifier",
Name: "function literal",
In: "GET / func() {} ()",
ExpMatch: true,
ExpErr: "expected function identifier",
},
{
Name: "call ellipsis",
In: "GET /{fileName} F(fileName...)",
ExpErr: "unexpected ellipsis",
Name: "call ellipsis",
In: "GET /{fileName} F(fileName...)",
ExpMatch: true,
ExpErr: "unexpected ellipsis",
},
} {
t.Run(tt.Name, func(t *testing.T) {
_, err, ok := muxt.NewTemplateName(tt.In)
require.True(t, ok)
if tt.ExpErr != "" {
assert.ErrorContains(t, err, tt.ExpErr)
} else {
assert.NoError(t, err)
if assert.Equal(t, tt.ExpMatch, ok) {
if tt.ExpErr != "" {
assert.ErrorContains(t, err, tt.ExpErr)
} else {
assert.NoError(t, err)
}
}
})
}
Expand Down

0 comments on commit daadcdf

Please sign in to comment.