summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMathias Magnusson <mathias@magnusson.space>2024-11-17 14:55:05 +0100
committerMathias Magnusson <mathias@magnusson.space>2024-11-17 14:55:05 +0100
commit72440d0e3b2a8224b65491202798f5c0b18a2dde (patch)
treee75675956ab77bf635ade8f2599dfa060a66f83c
parent14db9705ae1c2a10bb5e62a66a72f5cbc7aa7b13 (diff)
downloadhh-72440d0e3b2a8224b65491202798f5c0b18a2dde.tar.gz
mkChangeItUp
-rw-r--r--cmd/generate/main.go128
-rw-r--r--examples/basic.go35
2 files changed, 126 insertions, 37 deletions
diff --git a/cmd/generate/main.go b/cmd/generate/main.go
index 9f1f839..67e745c 100644
--- a/cmd/generate/main.go
+++ b/cmd/generate/main.go
@@ -8,7 +8,11 @@ import (
"go/parser"
"go/scanner"
"go/token"
+ "io"
"net/http"
+ "os"
+ "reflect"
+ "slices"
"strconv"
"strings"
)
@@ -19,59 +23,121 @@ func main() {
}
}
+func slice(fileContents string, filePosInfo *token.File, start token.Pos, end token.Pos) string {
+ return fileContents[filePosInfo.Position(start).Offset:filePosInfo.Position(end).Offset]
+}
+
func run() error {
+ filename := "examples/basic.go"
+ fileBytes, err := os.ReadFile(filename)
+ if err != nil {
+ return err
+ }
+ // fileContents := string(fileBytes)
var fset token.FileSet
- f, err := parser.ParseFile(&fset, "examples/basic.go", nil, parser.ParseComments|parser.SkipObjectResolution)
+ fp := fset.AddFile(filename, -1, len(fileBytes))
+ _ = fp
+ f, err := parser.ParseFile(&fset, "examples/basic.go", fileBytes, parser.ParseComments|parser.SkipObjectResolution)
if err != nil {
return err
}
+ var output bytes.Buffer
+ output.WriteString("package ")
+ output.WriteString(f.Name.Name)
+ output.WriteByte('\n')
for _, decl := range f.Decls {
f, ok := decl.(*ast.FuncDecl)
if !ok {
continue
}
+ if f.Doc == nil {
+ continue
+ }
hhRoute := f.Doc.List[len(f.Doc.List)-1].Text
var routeSpec string
if routeSpec, ok = strings.CutPrefix(hhRoute, "//hh:route "); !ok {
continue
}
- rs, err := parseRouteSpec(routeSpec)
- if err != nil {
- return err
+ split := strings.Split(routeSpec, " ")
+ var method, path string
+ if len(split) == 1 {
+ path = split[0]
+ } else if len(split) == 2 {
+ method = split[0]
+ path = split[1]
+ } else {
+ return errors.New("Invalid route spec. Expected `//hh:route [method] [path]` or `//hh:route [path]`")
}
- var wrapper bytes.Buffer
- wrapper.WriteString("func httpWrapper_")
- wrapper.WriteString(f.Name.String())
- wrapper.WriteString("[S any](s S, r *http.Request, w http.ResponseWriter) {")
- i := 0
- for _, p := range f.Type.Params.List {
- for _, n := range p.Names {
- wrapper.WriteString("\n\tvar")
- wrapper.WriteString(strconv.Itoa(i))
- wrapper.WriteString(" := ")
- wrapper.WriteString(n.Name)
- i++
+ if !slices.ContainsFunc([]string{
+ "",
+ http.MethodGet,
+ http.MethodHead,
+ http.MethodPost,
+ http.MethodPut,
+ http.MethodPatch,
+ http.MethodDelete,
+ http.MethodConnect,
+ http.MethodOptions,
+ http.MethodTrace,
+ }, func(m string) bool { return m == method }) {
+ return errors.New("Invalid http method " + method)
+ }
+ output.WriteString("\nfunc hh_")
+ output.WriteString(f.Name.String())
+ output.WriteString("[S any](s S, w http.ResponseWriter, r *http.Request) {")
+ parsedRequestType, ok := f.Type.Params.List[1].Type.(*ast.StructType)
+ if !ok {
+ return errors.New("Parsed request type must be a struct")
+ }
+ for _, field := range parsedRequestType.Fields.List {
+ for _, nameIdent := range field.Names {
+ typ := field.Type
+ name := nameIdent.Name
+ var tag string
+ if field.Tag != nil {
+ tag = reflect.StructTag(field.Tag.Value[1 : len(field.Tag.Value)-1]).Get("hh")
+ }
+ fmt.Println(typ, name, tag)
+ tags := strings.Split(tag, ",")
+ // TODO: handle raw request. Or maybe that should be a separate parameter
+ if len(tags) == 0 {
+ return errors.New("Don't know what to do with '" + name + "'. You must add a tag to specify")
+ }
+ switch tags[0] {
+ case "form":
+ output.WriteString("\n\t")
+ output.WriteString(name)
+ output.WriteString(" := r.FormValue(\"")
+ output.WriteString(name)
+ output.WriteString("\")")
+ case "cookie":
+ // panic("todo")
+ }
}
}
- wrapper.WriteString("\t")
- wrapper.WriteString(f.Name.Name)
- wrapper.WriteString("(")
- i = 0
- for _, p := range f.Type.Params.List {
- for range p.Names {
- if i > 0 {
- wrapper.WriteString(", ")
- }
- wrapper.WriteString("var")
- wrapper.WriteString(strconv.Itoa(i))
- i++
+ i := 0
+ output.WriteString("\n\t")
+ output.WriteString(f.Name.Name)
+ output.WriteString("(w, ")
+ i = 0
+ for _, field := range parsedRequestType.Fields.List {
+ for _, nameIdent := range field.Names {
+ typ := field.Type
+ name := nameIdent.Name
+ if i > 0 {
+ output.WriteString(", ")
+ }
+ output.WriteString("var")
+ output.WriteString(strconv.Itoa(i))
+ i++
}
}
- wrapper.WriteString(")\n")
- wrapper.WriteString("}\n")
+ output.WriteString(")\n")
+ output.WriteString("}\n")
- fmt.Printf("`%v`\n`%v`\n`%v`\n", routeSpec, f.Name.Name, rs)
+ fmt.Printf("`%v`\n`%v`\n", path, f.Name.Name)
}
+ io.Copy(os.Stdout, &output)
return nil
}
diff --git a/examples/basic.go b/examples/basic.go
index 467460f..2515c52 100644
--- a/examples/basic.go
+++ b/examples/basic.go
@@ -3,15 +3,38 @@ package examples
import (
"log/slog"
"net/http"
+ "strconv"
)
// Big bungus function here!
//
-//hh:route GET /admin/users, query: query, nextURL: hh.Cookie(r, "logout_next_url")
-func adminUsersForm(query struct {
- search, year string
- offset int
-}, w http.ResponseWriter) {
+//hh:route GET /admin/users
+func adminUsersForm(w http.ResponseWriter, r struct {
+ r *http.Request
+ search, year string `hh:"form"`
+ offset int `hh:"form,optional"`
+ nextURL string `hh:"cookie,logout_next_url"`
+}) {
_, _ = w.Write([]byte("ahahaha"))
- slog.Info("get admin users form", "search", query.search, "offset", query.offset)
+ slog.Info("get admin users form", "search", r.search, "offset", r.offset, "next-url", r.nextURL)
+}
+
+func hh_adminUsersForm[S any](s S, w http.ResponseWriter, r *http.Request) {
+ search := r.FormValue("search")
+
+ year := r.FormValue("year")
+
+ offset0 := r.FormValue("offset")
+ offset, err := strconv.Atoi(offset0)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte(`Bad request. Invalid integer 'offset' in form/query`))
+ }
+
+ adminUsersForm(w, struct {
+ r *http.Request
+ search, year string `hh:"form"`
+ offset int `hh:"form,optional"`
+ nextURL string `hh:"cookie,logout_next_url"`
+ }{r: r, search: search, year: year, offset: offset})
}