forked from mystiq/dex
160 lines
3 KiB
Go
160 lines
3 KiB
Go
|
package gendoc
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"fmt"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"text/template"
|
||
|
"unicode"
|
||
|
)
|
||
|
|
||
|
var funcs = template.FuncMap{
|
||
|
"renderJSON": func(i interface{}) string {
|
||
|
if s, ok := i.(Schema); ok {
|
||
|
return s.toJSON()
|
||
|
}
|
||
|
return ""
|
||
|
},
|
||
|
"toLink": toLink,
|
||
|
"toCodeStr": func(code int) string {
|
||
|
if code == CodeDefault {
|
||
|
return "default"
|
||
|
}
|
||
|
return strconv.Itoa(code)
|
||
|
},
|
||
|
}
|
||
|
|
||
|
var markdownTmpl = template.Must(template.New("md").Funcs(funcs).Parse(`
|
||
|
# {{ .Title }}
|
||
|
|
||
|
{{ .Description }}
|
||
|
|
||
|
__Version:__ {{ .Version }}
|
||
|
|
||
|
## Models
|
||
|
|
||
|
{{ range $i, $model := .Models }}
|
||
|
### {{ $model.Name }}
|
||
|
|
||
|
{{ $model.Description }}
|
||
|
|
||
|
{{ $model | renderJSON }}
|
||
|
{{ end }}
|
||
|
|
||
|
## Paths
|
||
|
|
||
|
{{ range $i, $path := .Paths }}
|
||
|
### {{ $path.Method }} {{ $path.Path }}
|
||
|
|
||
|
> __Summary__
|
||
|
|
||
|
> {{ $path.Summary }}
|
||
|
|
||
|
> __Description__
|
||
|
|
||
|
> {{ $path.Description }}
|
||
|
|
||
|
{{ if $path.Parameters }}
|
||
|
> __Parameters__
|
||
|
|
||
|
> |Name|Located in|Description|Required|Type|
|
||
|
|:-----|:-----|:-----|:-----|:-----|
|
||
|
{{ range $i, $p := $path.Parameters }}| {{ $p.Name }} | {{ $p.LocatedIn }} | {{ $p.Description }} | {{ if $p.Required }}Yes{{ else }}No{{ end }} | {{ $p.Type | toLink }} |
|
||
|
{{ end }}
|
||
|
{{ end }}
|
||
|
> __Responses__
|
||
|
|
||
|
> |Code|Description|Type|
|
||
|
|:-----|:-----|:-----|
|
||
|
{{ range $i, $r := $path.Responses }}| {{ $r.Code | toCodeStr }} | {{ $r.Description }} | {{ $r.Type | toLink }} |
|
||
|
{{ end }}
|
||
|
{{ end }}
|
||
|
`))
|
||
|
|
||
|
func (doc Document) MarshalMarkdown() ([]byte, error) {
|
||
|
var b bytes.Buffer
|
||
|
if err := markdownTmpl.Execute(&b, doc); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return b.Bytes(), nil
|
||
|
}
|
||
|
|
||
|
func (m Schema) toJSON() string {
|
||
|
var b bytes.Buffer
|
||
|
b.WriteString("```\n")
|
||
|
m.writeJSON(&b, "", true)
|
||
|
b.WriteString("\n```")
|
||
|
return b.String()
|
||
|
}
|
||
|
|
||
|
var indentStr = " "
|
||
|
|
||
|
func (m Schema) writeJSON(b *bytes.Buffer, indent string, first bool) {
|
||
|
if m.Ref != "" {
|
||
|
b.WriteString(m.Ref)
|
||
|
return
|
||
|
}
|
||
|
if first {
|
||
|
b.WriteString(indent)
|
||
|
}
|
||
|
|
||
|
switch m.Type {
|
||
|
case TypeArray:
|
||
|
b.WriteString("[")
|
||
|
for i, c := range m.Children {
|
||
|
if i > 0 {
|
||
|
b.WriteString(",")
|
||
|
}
|
||
|
b.WriteString("\n")
|
||
|
b.WriteString(indent + indentStr)
|
||
|
c.writeJSON(b, indent+indentStr, false)
|
||
|
}
|
||
|
b.WriteString("\n" + indent + "]")
|
||
|
case TypeObject:
|
||
|
b.WriteString("{")
|
||
|
for i, c := range m.Children {
|
||
|
if i > 0 {
|
||
|
b.WriteString(",")
|
||
|
}
|
||
|
b.WriteString("\n")
|
||
|
b.WriteString(indent + indentStr + c.Name + ": ")
|
||
|
c.writeJSON(b, indent+indentStr, false)
|
||
|
}
|
||
|
b.WriteString("\n" + indent + "}")
|
||
|
case TypeBool, TypeFloat, TypeInt, TypeString:
|
||
|
b.WriteString(m.Type)
|
||
|
if m.Description != "" {
|
||
|
b.WriteString(" // ")
|
||
|
b.WriteString(m.Description)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func toAnchor(s string) string {
|
||
|
var b bytes.Buffer
|
||
|
r := strings.NewReader(s)
|
||
|
for {
|
||
|
r, _, err := r.ReadRune()
|
||
|
if err != nil {
|
||
|
return b.String()
|
||
|
}
|
||
|
switch {
|
||
|
case r == ' ':
|
||
|
b.WriteRune('-')
|
||
|
case 'a' <= r && r <= 'z':
|
||
|
b.WriteRune(r)
|
||
|
case 'A' <= r && r <= 'Z':
|
||
|
b.WriteRune(unicode.ToLower(r))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func toLink(s string) string {
|
||
|
switch s {
|
||
|
case "string", "boolean", "integer", "":
|
||
|
return s
|
||
|
}
|
||
|
return fmt.Sprintf("[%s](#%s)", s, toAnchor(s))
|
||
|
}
|