172 lines
3.9 KiB
Go
172 lines
3.9 KiB
Go
package apigen
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
apierrors "git.yuntuo.hk/k2safe/go-omni/errors"
|
|
)
|
|
|
|
// ErrorDefinition declares a public, stable error. Generated constructors return
|
|
// a fresh error each time so WithCause cannot mutate a shared singleton.
|
|
type ErrorDefinition struct {
|
|
Name string `json:"name"`
|
|
BusinessCode any `json:"businessCode,omitempty"`
|
|
Params []apierrors.Parameter `json:"params,omitempty"`
|
|
Translation *apierrors.Translation `json:"translation,omitempty"`
|
|
Catalog string `json:"catalog,omitempty"`
|
|
Status int `json:"status"`
|
|
Code string `json:"code"`
|
|
Message string `json:"message"`
|
|
Position string `json:"-"`
|
|
}
|
|
type AuthDefinition struct {
|
|
Name, Type, In, KeyName, Middleware, Description, BearerFormat, Position string
|
|
}
|
|
|
|
func (p *parser) parseErrors() error {
|
|
p.next()
|
|
block := p.peek() == "("
|
|
if block {
|
|
p.next()
|
|
p.skip()
|
|
}
|
|
for {
|
|
pos := p.tokens[p.at].position
|
|
name, err := p.ident()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
status, err := strconv.Atoi(p.next().text)
|
|
if err != nil {
|
|
return p.err("error HTTP status must be an integer")
|
|
}
|
|
code, err := p.quoted()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
message, err := p.quoted()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.spec.Errors = append(p.spec.Errors, ErrorDefinition{Name: name, Status: status, Code: code, Message: message, Position: pos})
|
|
if p.peek() != "\n" && p.peek() != ";" && !(block && p.peek() == ")") && p.peek() != "" {
|
|
return p.err("expected end of error declaration")
|
|
}
|
|
if !block {
|
|
return nil
|
|
}
|
|
p.skip()
|
|
if p.peek() == ")" {
|
|
p.next()
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
func (p *parser) parseAuth() error {
|
|
pos := p.next().position
|
|
name, err := p.ident()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
p.skip()
|
|
props, err := p.metadata()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
a := AuthDefinition{Name: name, Position: pos}
|
|
for key, value := range props {
|
|
switch key {
|
|
case "type":
|
|
a.Type = value
|
|
case "in":
|
|
a.In = value
|
|
case "name":
|
|
a.KeyName = value
|
|
case "middleware":
|
|
a.Middleware = value
|
|
case "description":
|
|
a.Description = value
|
|
case "bearerFormat":
|
|
a.BearerFormat = value
|
|
default:
|
|
return fmt.Errorf("%s: unsupported auth property %q", pos, key)
|
|
}
|
|
}
|
|
p.spec.Auth = append(p.spec.Auth, a)
|
|
return nil
|
|
}
|
|
func (p *parser) names() ([]string, error) {
|
|
var names []string
|
|
for {
|
|
name, err := p.ident()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, prior := range names {
|
|
if prior == name {
|
|
return nil, p.err("duplicate name " + name)
|
|
}
|
|
}
|
|
names = append(names, name)
|
|
if p.peek() != "," {
|
|
return names, nil
|
|
}
|
|
p.next()
|
|
}
|
|
}
|
|
func commaList(value string) []string {
|
|
if value == "" {
|
|
return nil
|
|
}
|
|
result := strings.Split(value, ",")
|
|
for i := range result {
|
|
result[i] = strings.TrimSpace(result[i])
|
|
}
|
|
return result
|
|
}
|
|
func mergeNames(base, extra []string) []string {
|
|
result := append([]string(nil), base...)
|
|
for _, name := range extra {
|
|
found := false
|
|
for _, prior := range result {
|
|
found = found || prior == name
|
|
}
|
|
if !found {
|
|
result = append(result, name)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
func (p *parser) parseDoc(route *Route) error {
|
|
if p.peek() != "(" {
|
|
value, err := p.quoted()
|
|
route.Summary = value
|
|
return err
|
|
}
|
|
pos := p.tokens[p.at].position
|
|
props, err := p.metadata()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for key, value := range props {
|
|
switch key {
|
|
case "summary":
|
|
route.Summary = value
|
|
case "description":
|
|
route.Description = value
|
|
case "tags":
|
|
route.Tags = commaList(value)
|
|
case "deprecated":
|
|
if value != "true" && value != "false" {
|
|
return fmt.Errorf("%s: deprecated must be true or false", pos)
|
|
}
|
|
route.Deprecated = value == "true"
|
|
default:
|
|
return fmt.Errorf("%s: unsupported @doc property %q", pos, key)
|
|
}
|
|
}
|
|
return nil
|
|
}
|