-
Notifications
You must be signed in to change notification settings - Fork 0
/
enrich.go
115 lines (102 loc) · 3.08 KB
/
enrich.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package cli
import (
"bufio"
"bytes"
"fmt"
"go/doc"
"os"
"reflect"
"strings"
)
// Enrich fills in Cmd and Opt fields runtime
// by parsing names and doc strings, looking for annotations
// such as Synopsis, Deprecated, Example, etc.
func Enrich(cmd *Cmd) {
enrichCmd(cmd)
for _, opt := range cmd.Opts {
enrichOpt(opt)
}
}
// enrichOpt parses an option's doc string for additional detail.
func enrichOpt(opt *Opt) {
opt.Synopsis = doc.Synopsis(opt.RawDoc)
var lines []string
scan := bufio.NewScanner(bytes.NewBufferString(opt.RawDoc))
for scan.Scan() {
line := strings.TrimSpace(scan.Text())
// TODO "Name" is more tricky for options, because you might also want
// to override the name of intermediate parents, e.g. Foo.BarBAZ.Bat
// there's no Opt for BarBAZ to be parsed. Probably should rely
// on struct tags, on a runtime builder, and/or parse struct field
// docs in the code generator.
//
// Also, struct tags can be reflected at runtime here in ParseOptDetail,
// if desired.
//if strings.HasPrefix(line, "Name: ") {
//det.Name = strings.TrimPrefix(line, "Name: ")
//}
switch {
case line == opt.Synopsis:
case line == "Hidden":
opt.Hidden = true
case strings.HasPrefix(line, "Deprecated: "):
opt.Deprecated = strings.TrimPrefix(line, "Deprecated: ")
default:
lines = append(lines, line)
}
}
opt.Doc = strings.TrimSpace(strings.Join(lines, "\n"))
switch {
case opt.DefaultValue == os.Stderr:
opt.DefaultString = "os.Stderr"
case opt.DefaultValue == os.Stdout:
opt.DefaultString = "os.Stdout"
case opt.DefaultValue == nil:
opt.DefaultString = ""
// TODO not super happy about including reflect just for this.
case reflect.TypeOf(opt.DefaultValue).Kind() == reflect.Ptr:
opt.DefaultString = ""
default:
opt.DefaultString = fmt.Sprintf("%v", opt.DefaultValue)
}
}
// enrichCmd parses a command's doc string for additional information.
func enrichCmd(cmd *Cmd) {
cmd.Synopsis = doc.Synopsis(cmd.RawDoc)
setNamePath := func(parts []string) {
if len(parts) == 0 {
return
}
cmd.Path = nil
for _, p := range parts {
cmd.Path = append(cmd.Path, strings.ToLower(p))
}
cmd.Name = cmd.Path[len(cmd.Path)-1]
}
parts := splitIdent(cmd.RawName)
setNamePath(parts)
var lines []string
scan := bufio.NewScanner(bytes.NewBufferString(cmd.RawDoc))
for scan.Scan() {
line := strings.TrimSpace(scan.Text())
switch {
case line == cmd.Synopsis:
case strings.HasPrefix(line, "Name: "):
name := strings.TrimPrefix(line, "Name: ")
parts := strings.Split(name, " ")
setNamePath(parts)
case strings.HasPrefix(line, "Deprecated: "):
cmd.Deprecated = strings.TrimPrefix(line, "Deprecated: ")
// TODO example could be multi line
case strings.HasPrefix(line, "Example: "):
cmd.Example = strings.TrimPrefix(line, "Example: ")
case line == "Hidden":
cmd.Hidden = true
case strings.HasPrefix(line, "Aliases: "):
cmd.Aliases = strings.Split(strings.TrimPrefix(line, "Aliases: "), " ")
default:
lines = append(lines, line)
}
}
cmd.Doc = strings.TrimSpace(strings.Join(lines, "\n"))
}