173 lines
4.3 KiB
Go
173 lines
4.3 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"text/tabwriter"
|
|
|
|
"git.yuntuo.hk/k2safe/go-omni/cmd/go-omni/apigen"
|
|
)
|
|
|
|
func runAPI(args []string) error {
|
|
if len(args) == 0 || args[0] == "help" || args[0] == "--help" || args[0] == "-h" {
|
|
usage()
|
|
return nil
|
|
}
|
|
command := args[0]
|
|
if command == "go" {
|
|
command = "generate"
|
|
}
|
|
if command == "check" {
|
|
command = "validate"
|
|
}
|
|
if command != "new" && command != "generate" && command != "validate" && command != "inspect" && command != "export" {
|
|
return fmt.Errorf("unknown api command %q", command)
|
|
}
|
|
readOnly := command == "validate" || command == "inspect" || command == "export"
|
|
o := apigen.Options{}
|
|
for i := 1; i < len(args); i++ {
|
|
arg := args[i]
|
|
if arg == "--help" || arg == "-h" {
|
|
usage()
|
|
return nil
|
|
}
|
|
if arg == "--check" || arg == "--dry-run" {
|
|
if command != "generate" {
|
|
return fmt.Errorf("%s belongs to api generate", arg)
|
|
}
|
|
if arg == "--check" {
|
|
o.Check = true
|
|
} else {
|
|
o.DryRun = true
|
|
}
|
|
continue
|
|
}
|
|
if !strings.HasPrefix(arg, "-") {
|
|
if command == "new" && o.Dir == "" {
|
|
o.Dir = arg
|
|
continue
|
|
}
|
|
return fmt.Errorf("unexpected argument %q", arg)
|
|
}
|
|
name, value, inline := strings.Cut(arg, "=")
|
|
// goctl-style -api/-dir aliases ease migration; long forms are canonical.
|
|
if name == "-api" {
|
|
name = "--api"
|
|
}
|
|
if name == "-dir" {
|
|
name = "--dir"
|
|
}
|
|
switch name {
|
|
case "--api", "--dir", "--module", "--framework-dir", "--framework-version", "--log-output", "--log-format", "--database", "--storage":
|
|
default:
|
|
return fmt.Errorf("unknown api option %q", name)
|
|
}
|
|
if !inline {
|
|
if i+1 >= len(args) || strings.HasPrefix(args[i+1], "-") {
|
|
return fmt.Errorf("%s requires a value", name)
|
|
}
|
|
i++
|
|
value = args[i]
|
|
}
|
|
if strings.TrimSpace(value) == "" {
|
|
return fmt.Errorf("%s requires a value", name)
|
|
}
|
|
if readOnly && name != "--api" {
|
|
return fmt.Errorf("api %s does not accept %s", command, name)
|
|
}
|
|
switch name {
|
|
case "--api":
|
|
o.API = value
|
|
case "--dir":
|
|
o.Dir = value
|
|
case "--module":
|
|
o.Module = value
|
|
case "--framework-dir":
|
|
o.FrameworkDir = value
|
|
case "--framework-version":
|
|
o.FrameworkVersion = value
|
|
case "--database":
|
|
o.Database = value
|
|
case "--storage":
|
|
o.Storage = value
|
|
case "--log-output":
|
|
o.LogOutput = value
|
|
case "--log-format":
|
|
o.LogFormat = value
|
|
}
|
|
}
|
|
if command != "new" && o.API == "" {
|
|
dir := o.Dir
|
|
if dir == "" {
|
|
dir = "."
|
|
}
|
|
var err error
|
|
o.API, err = apigen.ProjectEntry(dir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if readOnly {
|
|
spec, err := apigen.Parse(o.API)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
compiled, err := apigen.Compile(spec)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch command {
|
|
case "validate":
|
|
fmt.Printf("valid API: %d types, %d routes, %d errors, %d auth schemes\n", len(spec.Types), len(spec.Routes), len(spec.Errors), len(spec.Auth))
|
|
case "export":
|
|
fmt.Println(string(compiled.Document))
|
|
case "inspect":
|
|
writer := tabwriter.NewWriter(os.Stdout, 0, 4, 2, ' ', 0)
|
|
fmt.Fprintln(writer, "METHOD\tPATH\tHANDLER\tAUTH\tERRORS")
|
|
definitions := map[string]apigen.ErrorDefinition{}
|
|
for _, e := range spec.Errors {
|
|
definitions[e.Name] = e
|
|
}
|
|
for _, r := range compiled.Spec.Routes {
|
|
auth := r.Auth
|
|
if auth == "" {
|
|
auth = "(undeclared)"
|
|
}
|
|
var errs []string
|
|
for _, name := range r.Errors {
|
|
e := definitions[name]
|
|
errs = append(errs, fmt.Sprintf("%d:%s", e.Status, e.Code))
|
|
}
|
|
fmt.Fprintf(writer, "%s\t%s\t%s/%s\t%s\t%s\n", strings.ToUpper(r.Method), r.Path, r.Group, r.Handler, auth, strings.Join(errs, ", "))
|
|
}
|
|
return writer.Flush()
|
|
}
|
|
return nil
|
|
}
|
|
var out apigen.Output
|
|
var err error
|
|
if command == "new" {
|
|
out, err = apigen.New(o)
|
|
} else {
|
|
if o.Dir == "" {
|
|
o.Dir = "."
|
|
}
|
|
out, err = apigen.Generate(o)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if o.DryRun {
|
|
for _, change := range out.Changes {
|
|
fmt.Printf("%-7s %s\n", change.Action, change.Path)
|
|
}
|
|
fmt.Printf("Preview: %d changes; %d existing developer-owned files preserved; no files written\n", len(out.Changes), len(out.Preserved))
|
|
} else if o.Check {
|
|
fmt.Println("API generated files are current")
|
|
} else {
|
|
fmt.Printf("API project ready: %d generated, %d created, %d preserved, %d removed\n", len(out.Generated), len(out.Created), len(out.Preserved), len(out.Removed))
|
|
}
|
|
return nil
|
|
}
|