155 lines
4.2 KiB
Go
155 lines
4.2 KiB
Go
package route
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/iancoleman/strcase"
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
type RenderBuildOpts struct {
|
|
PackageName string
|
|
ProjectPackage string
|
|
Routes []RouteDefinition
|
|
}
|
|
|
|
func buildRenderData(opts RenderBuildOpts) (RenderData, error) {
|
|
rd := RenderData{
|
|
PackageName: opts.PackageName,
|
|
ProjectPackage: opts.ProjectPackage,
|
|
Imports: []string{},
|
|
Controllers: []string{},
|
|
Routes: make(map[string][]Router),
|
|
RouteGroups: []string{},
|
|
}
|
|
|
|
imports := []string{}
|
|
controllers := []string{}
|
|
// Track if any param uses model lookup, which requires the field package.
|
|
needsFieldImport := false
|
|
|
|
for _, route := range opts.Routes {
|
|
imports = append(imports, route.Imports...)
|
|
controllers = append(controllers, fmt.Sprintf("%s *%s", strcase.ToLowerCamel(route.Name), route.Name))
|
|
|
|
for _, action := range route.Actions {
|
|
funcName := fmt.Sprintf("Func%d", len(action.Params))
|
|
if action.HasData {
|
|
funcName = "Data" + funcName
|
|
}
|
|
|
|
params := lo.FilterMap(action.Params, func(item ParamDefinition, _ int) (string, bool) {
|
|
tok := buildParamToken(item)
|
|
if tok == "" {
|
|
return "", false
|
|
}
|
|
if item.Model != "" {
|
|
needsFieldImport = true
|
|
}
|
|
return tok, true
|
|
})
|
|
|
|
rd.Routes[route.Name] = append(rd.Routes[route.Name], Router{
|
|
Method: strcase.ToCamel(action.Method),
|
|
Route: action.Route,
|
|
Controller: strcase.ToLowerCamel(route.Name),
|
|
Action: action.Name,
|
|
Func: funcName,
|
|
Params: params,
|
|
})
|
|
}
|
|
}
|
|
|
|
// Add field import if any model lookups are used
|
|
if needsFieldImport {
|
|
imports = append(imports, `field "go.ipao.vip/gen/field"`)
|
|
}
|
|
|
|
// de-dup and sort imports/controllers for stable output
|
|
rd.Imports = lo.Uniq(imports)
|
|
sort.Strings(rd.Imports)
|
|
rd.Controllers = lo.Uniq(controllers)
|
|
sort.Strings(rd.Controllers)
|
|
|
|
// stable order for route groups and entries
|
|
for k := range rd.Routes {
|
|
rd.RouteGroups = append(rd.RouteGroups, k)
|
|
}
|
|
sort.Strings(rd.RouteGroups)
|
|
for _, k := range rd.RouteGroups {
|
|
items := rd.Routes[k]
|
|
sort.Slice(items, func(i, j int) bool {
|
|
if items[i].Method != items[j].Method {
|
|
return items[i].Method < items[j].Method
|
|
}
|
|
if items[i].Route != items[j].Route {
|
|
return items[i].Route < items[j].Route
|
|
}
|
|
return items[i].Action < items[j].Action
|
|
})
|
|
rd.Routes[k] = items
|
|
}
|
|
|
|
return rd, nil
|
|
}
|
|
|
|
func buildParamToken(item ParamDefinition) string {
|
|
key := item.Name
|
|
if item.Key != "" {
|
|
key = item.Key
|
|
}
|
|
|
|
switch item.Position {
|
|
case PositionQuery:
|
|
return fmt.Sprintf(`Query%s[%s]("%s")`, scalarSuffix(item.Type), item.Type, key)
|
|
case PositionHeader:
|
|
return fmt.Sprintf(`Header[%s]("%s")`, item.Type, key)
|
|
case PositionFile:
|
|
return fmt.Sprintf(`File[multipart.FileHeader]("%s")`, key)
|
|
case PositionCookie:
|
|
if item.Type == "string" {
|
|
return fmt.Sprintf(`CookieParam("%s")`, key)
|
|
}
|
|
return fmt.Sprintf(`Cookie[%s]("%s")`, item.Type, key)
|
|
case PositionBody:
|
|
return fmt.Sprintf(`Body[%s]("%s")`, item.Type, key)
|
|
case PositionPath:
|
|
// If a model field is specified, generate a model-lookup binder from path value.
|
|
if item.Model != "" {
|
|
field := "id"
|
|
fieldType := "int"
|
|
if strings.Contains(item.Model, ":") {
|
|
parts := strings.SplitN(item.Model, ":", 2)
|
|
if len(parts) == 2 {
|
|
field = parts[0]
|
|
fieldType = parts[1]
|
|
}
|
|
} else {
|
|
field = item.Model
|
|
}
|
|
|
|
tpl := `func(ctx fiber.Ctx) (*%s, error) {
|
|
v := fiber.Params[%s](ctx, "%s")
|
|
return %sQuery.WithContext(ctx).Where(field.NewUnsafeFieldRaw("%s = ?", v)).First()
|
|
}`
|
|
return fmt.Sprintf(tpl, item.Type, fieldType, key, item.Type, field)
|
|
}
|
|
return fmt.Sprintf(`Path%s[%s]("%s")`, scalarSuffix(item.Type), item.Type, key)
|
|
case PositionLocal:
|
|
return fmt.Sprintf(`Local[%s]("%s")`, item.Type, key)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func scalarSuffix(t string) string {
|
|
switch t {
|
|
case "string", "int", "int8", "int16", "int32", "int64",
|
|
"uint", "uint8", "uint16", "uint32", "uint64",
|
|
"float32", "float64", "bool":
|
|
return "Param"
|
|
}
|
|
return ""
|
|
}
|