Some checks are pending
Docs Deploy / build_and_deploy (push) Waiting to run
Generate Docs / cli (push) Waiting to run
Generate Config Doc / cli (push) Waiting to run
Go formatting / go-formatting (push) Waiting to run
Check links / markdown-link-check (push) Waiting to run
Integration / pre-test (push) Waiting to run
Integration / test on (push) Blocked by required conditions
Integration / status (push) Blocked by required conditions
Lint / Lint Go code (push) Waiting to run
Test / test (ubuntu-latest) (push) Waiting to run
135 lines
3.1 KiB
Go
135 lines
3.1 KiB
Go
package clidoc
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/ignite/cli/v29/ignite/pkg/errors"
|
|
)
|
|
|
|
const listSuffix = "list"
|
|
|
|
type (
|
|
// Docs represents a slice of Doc.
|
|
Docs []Doc
|
|
// Doc represents the struct documentation with tag comments.
|
|
Doc struct {
|
|
Key string
|
|
Type string
|
|
Value Docs
|
|
Comment string
|
|
}
|
|
)
|
|
|
|
// String converts Docs to a string.
|
|
func (d Docs) String() string {
|
|
var sb strings.Builder
|
|
// Initial call with a negative level to avoid unwanted dash at the top level
|
|
d.writeString(&sb, -1)
|
|
return strings.TrimSpace(sb.String())
|
|
}
|
|
|
|
// writeString appends the contents of Docs to sb's buffer at level.
|
|
func (d Docs) writeString(sb *strings.Builder, level int) {
|
|
indent := strings.Repeat(" ", level+1) // Two spaces per YAML indentation standard
|
|
for _, doc := range d {
|
|
sb.WriteString(indent)
|
|
switch doc.Type {
|
|
case "":
|
|
sb.WriteString(fmt.Sprintf("%s: # %s\n", doc.Key, doc.Comment))
|
|
default:
|
|
sb.WriteString(fmt.Sprintf("%s: (%s) # %s\n", doc.Key, doc.Type, doc.Comment))
|
|
}
|
|
|
|
if len(doc.Value) > 0 {
|
|
doc.Value.writeString(sb, level+1)
|
|
}
|
|
}
|
|
}
|
|
|
|
// GenDoc to generate documentation from a struct.
|
|
func GenDoc(v interface{}) (fields Docs, err error) {
|
|
t := reflect.TypeOf(v)
|
|
if t.Kind() != reflect.Struct && t.Kind() != reflect.Ptr {
|
|
return fields, nil
|
|
}
|
|
for i := 0; i < t.NumField(); i++ {
|
|
var (
|
|
field = t.Field(i)
|
|
yaml = field.Tag.Get("yaml")
|
|
doc = field.Tag.Get("doc")
|
|
)
|
|
|
|
tags := strings.Split(yaml, ",")
|
|
if len(tags) == 0 {
|
|
return fields, errors.Errorf("no tags found in struct field %s", field.Name)
|
|
}
|
|
name := tags[0]
|
|
if name == "" {
|
|
name = strings.ToLower(field.Name)
|
|
}
|
|
if len(tags) > 1 && strings.Contains(tags[1], "inline") {
|
|
elemFields, err := GenDoc(reflect.New(field.Type).Elem().Interface())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
fields = append(fields, elemFields...)
|
|
continue
|
|
}
|
|
|
|
var (
|
|
elemFields Docs
|
|
elemType string
|
|
)
|
|
switch field.Type.Kind() { //nolint
|
|
case reflect.Struct:
|
|
elemType = field.Type.Kind().String()
|
|
elemFields, err = GenDoc(reflect.New(field.Type).Elem().Interface())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
case reflect.Ptr:
|
|
elemType = field.Type.Elem().Kind().String()
|
|
elemFields, err = GenDoc(reflect.New(field.Type.Elem()).Elem().Interface())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
case reflect.Slice:
|
|
elemType = listName(field.Type.Elem().Kind().String())
|
|
elemFields, err = GenDoc(reflect.New(field.Type.Elem()).Elem().Interface())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
default:
|
|
elemType = field.Type.Kind().String()
|
|
}
|
|
fields = append(fields, Doc{
|
|
Key: name,
|
|
Comment: doc,
|
|
Value: elemFields,
|
|
Type: mapTypes(elemType),
|
|
})
|
|
}
|
|
|
|
return fields, nil
|
|
}
|
|
|
|
func listName(name string) string {
|
|
return fmt.Sprintf("%s %s", name, listSuffix)
|
|
}
|
|
|
|
func mapTypes(doc string) string {
|
|
docTypes := map[string]string{
|
|
"struct": "",
|
|
"map": "key/value",
|
|
"slice": listSuffix,
|
|
listName("map"): listSuffix,
|
|
listName("slice"): listSuffix,
|
|
listName("struct"): listSuffix,
|
|
}
|
|
if docType, ok := docTypes[doc]; ok {
|
|
return docType
|
|
}
|
|
return doc
|
|
}
|