diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..1c4cb51 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,8 @@ +# Examples + +Each directory contains a sample flagset/command setup and the resulting +generated docs. + +- [flagset](flagset) - Using only stdlib `flag.FlagSet` +- [single](single) - Using a single `ffcli.Command` +- [multiple](multiple) - Using `ffcli.Command` with multiple sub-commands \ No newline at end of file diff --git a/examples/command.md b/examples/command.md deleted file mode 100644 index dfc5db3..0000000 --- a/examples/command.md +++ /dev/null @@ -1,55 +0,0 @@ -# myapp - -myapp - -``` -myapp -``` - - -``` -[--help] -[--myapp-bool-flag-f] -[--myapp-bool-flag-t] -[--myapp-duration-flag]=[value] -[--myapp-duration-flag-default]=[value] -[--myapp-int-flag]=[value] -[--myapp-int-flag-default]=[value] -[--myapp-string-flag]=[value] -[--myapp-string-flag-default]=[value] -``` -**Usage**: - -``` -myapp [FLAGS] [ARGS...] -``` - -**--help**: Show help - - -**--myapp-bool-flag-f**: Bool flag false - - -**--myapp-bool-flag-t**: Bool flag true (default: `true`) - - -**--myapp-duration-flag**="": Duration flag with no default - - -**--myapp-duration-flag-default**="": Duration flag with default (default: `5m0s`) - - -**--myapp-int-flag**="": Int flag with no default - - -**--myapp-int-flag-default**="": Int flag with default (default: `100`) - - -**--myapp-string-flag**="": String flag with no default - - -**--myapp-string-flag-default**="": String flag with default (default: `string default`) - - ------ - diff --git a/examples/flagset.md b/examples/flagset/flagset.md similarity index 100% rename from examples/flagset.md rename to examples/flagset/flagset.md diff --git a/examples/flagset/main.go b/examples/flagset/main.go new file mode 100644 index 0000000..ef84da8 --- /dev/null +++ b/examples/flagset/main.go @@ -0,0 +1,44 @@ +//go:build generate +// +build generate + +package main + +import ( + "flag" + "os" + "time" + + "go.jolheiser.com/ffmd" +) + +//go:generate go run main.go +func main() { + fs := flag.NewFlagSet("myapp", flag.ExitOnError) + fs.String("string-flag", "", "String flag with no default") + fs.String("string-flag-default", "string default", "String flag with default") + fs.Int("int-flag", 0, "Int flag with no default") + fs.Int("int-flag-default", 100, "Int flag with default") + fs.Bool("bool-flag-f", false, "Bool flag false") + fs.Bool("bool-flag-t", true, "Bool flag true") + fs.Duration("duration-flag", 0, "Duration flag with no default") + fs.Duration("duration-flag-default", time.Minute*5, "Duration flag with default") + + md, err := ffmd.FromFlagSet(fs) + if err != nil { + panic(err) + } + + write("flagset.md", md) +} + +func write(path, content string) { + fi, err := os.Create(path) + if err != nil { + panic(err) + } + defer fi.Close() + if _, err := fi.WriteString(content); err != nil { + panic(err) + } +} + diff --git a/examples/command-sub.md b/examples/multiple/command-sub.md similarity index 100% rename from examples/command-sub.md rename to examples/multiple/command-sub.md diff --git a/examples/examples.go b/examples/multiple/main.go similarity index 79% rename from examples/examples.go rename to examples/multiple/main.go index 652e362..60a4b91 100644 --- a/examples/examples.go +++ b/examples/multiple/main.go @@ -14,59 +14,8 @@ import ( "go.jolheiser.com/ffmd" ) -//go:generate go run examples.go +//go:generate go run main.go func main() { - fs := flagSet("") - md, err := ffmd.FromFlagSet(fs) - if err != nil { - panic(err) - } - write("flagset.md", md) - - command() - commandSub() -} - -func write(path, content string) { - fi, err := os.Create(path) - if err != nil { - panic(err) - } - defer fi.Close() - if _, err := fi.WriteString(content); err != nil { - panic(err) - } -} - -func flagSet(name string) *flag.FlagSet { - fs := flag.NewFlagSet("myapp", flag.ExitOnError) - fs.String(fmt.Sprintf("%sstring-flag", name), "", "String flag with no default") - fs.String(fmt.Sprintf("%sstring-flag-default", name), "string default", "String flag with default") - fs.Int(fmt.Sprintf("%sint-flag", name), 0, "Int flag with no default") - fs.Int(fmt.Sprintf("%sint-flag-default", name), 100, "Int flag with default") - fs.Bool(fmt.Sprintf("%sbool-flag-f", name), false, "Bool flag false") - fs.Bool(fmt.Sprintf("%sbool-flag-t", name), true, "Bool flag true") - fs.Duration(fmt.Sprintf("%sduration-flag", name), 0, "Duration flag with no default") - fs.Duration(fmt.Sprintf("%sduration-flag-default", name), time.Minute*5, "Duration flag with default") - return fs -} - -func command() { - fs1 := flagSet("myapp-") - cmd1 := &ffcli.Command{ - Name: "myapp", - FlagSet: fs1, - Subcommands: nil, - } - - md, err := ffmd.FromCommand(cmd1) - if err != nil { - panic(err) - } - write("command.md", md) -} - -func commandSub() { fs := flagSet("myapp-") cmd := &ffcli.Command{ Name: "myapp", @@ -95,18 +44,18 @@ func commandSub() { } fs4 := flagSet("sub4-") cmd4 := &ffcli.Command{ - Name: "sub4", - FlagSet: fs4, + Name: "sub4", + FlagSet: fs4, } fs5 := flagSet("sub5-") cmd5 := &ffcli.Command{ - Name: "sub5", - FlagSet: fs5, + Name: "sub5", + FlagSet: fs5, } fs6 := flagSet("sub6-") cmd6 := &ffcli.Command{ - Name: "sub6", - FlagSet: fs6, + Name: "sub6", + FlagSet: fs6, } cmd.Subcommands = []*ffcli.Command{cmd1, cmd2, cmd6} @@ -119,3 +68,27 @@ func commandSub() { } write("command-sub.md", md) } + +func write(path, content string) { + fi, err := os.Create(path) + if err != nil { + panic(err) + } + defer fi.Close() + if _, err := fi.WriteString(content); err != nil { + panic(err) + } +} + +func flagSet(name string) *flag.FlagSet { + fs := flag.NewFlagSet("myapp", flag.ExitOnError) + fs.String(fmt.Sprintf("%sstring-flag", name), "", "String flag with no default") + fs.String(fmt.Sprintf("%sstring-flag-default", name), "string default", "String flag with default") + fs.Int(fmt.Sprintf("%sint-flag", name), 0, "Int flag with no default") + fs.Int(fmt.Sprintf("%sint-flag-default", name), 100, "Int flag with default") + fs.Bool(fmt.Sprintf("%sbool-flag-f", name), false, "Bool flag false") + fs.Bool(fmt.Sprintf("%sbool-flag-t", name), true, "Bool flag true") + fs.Duration(fmt.Sprintf("%sduration-flag", name), 0, "Duration flag with no default") + fs.Duration(fmt.Sprintf("%sduration-flag-default", name), time.Minute*5, "Duration flag with default") + return fs +} \ No newline at end of file diff --git a/examples/single/main.go b/examples/single/main.go new file mode 100644 index 0000000..0d49f64 --- /dev/null +++ b/examples/single/main.go @@ -0,0 +1,50 @@ +//go:build generate +// +build generate + +package main + +import ( + "flag" + "os" + "time" + + "github.com/peterbourgon/ff/v3/ffcli" + + "go.jolheiser.com/ffmd" +) + +//go:generate go run main.go +func main() { + fs := flag.NewFlagSet("myapp", flag.ExitOnError) + fs.String("string-flag", "", "String flag with no default") + fs.String("string-flag-default", "string default", "String flag with default") + fs.Int("int-flag", 0, "Int flag with no default") + fs.Int("int-flag-default", 100, "Int flag with default") + fs.Bool("bool-flag-f", false, "Bool flag false") + fs.Bool("bool-flag-t", true, "Bool flag true") + fs.Duration("duration-flag", 0, "Duration flag with no default") + fs.Duration("duration-flag-default", time.Minute*5, "Duration flag with default") + + cmd := &ffcli.Command{ + Name: "myapp", + FlagSet: fs, + Subcommands: nil, + } + + md, err := ffmd.FromCommand(cmd) + if err != nil { + panic(err) + } + write("single.md", md) +} + +func write(path, content string) { + fi, err := os.Create(path) + if err != nil { + panic(err) + } + defer fi.Close() + if _, err := fi.WriteString(content); err != nil { + panic(err) + } +} diff --git a/examples/single/single.md b/examples/single/single.md new file mode 100644 index 0000000..3dfb23b --- /dev/null +++ b/examples/single/single.md @@ -0,0 +1,55 @@ +# myapp + +myapp + +``` +myapp +``` + + +``` +[--bool-flag-f] +[--bool-flag-t] +[--duration-flag]=[value] +[--duration-flag-default]=[value] +[--help] +[--int-flag]=[value] +[--int-flag-default]=[value] +[--string-flag]=[value] +[--string-flag-default]=[value] +``` +**Usage**: + +``` +myapp [FLAGS] [ARGS...] +``` + +**--bool-flag-f**: Bool flag false + + +**--bool-flag-t**: Bool flag true (default: `true`) + + +**--duration-flag**="": Duration flag with no default + + +**--duration-flag-default**="": Duration flag with default (default: `5m0s`) + + +**--help**: Show help + + +**--int-flag**="": Int flag with no default + + +**--int-flag-default**="": Int flag with default (default: `100`) + + +**--string-flag**="": String flag with no default + + +**--string-flag-default**="": String flag with default (default: `string default`) + + +----- + diff --git a/ffmd_test.go b/ffmd_test.go deleted file mode 100644 index 9b7a9ef..0000000 --- a/ffmd_test.go +++ /dev/null @@ -1 +0,0 @@ -package ffmd diff --git a/tree.go b/tree.go index f12e61d..659736a 100644 --- a/tree.go +++ b/tree.go @@ -2,49 +2,27 @@ package ffmd import ( "fmt" - "github.com/peterbourgon/ff/v3/ffcli" "sort" "strings" + + "github.com/peterbourgon/ff/v3/ffcli" ) // Tree returns a tree-representation of a ffcli.Command func Tree(cmd *ffcli.Command) string { - t := tree{} - for _, path := range commandPaths(cmd, "") { - t.add(path) - } + t := tree{cmd.Name: commandTree(cmd)} s := t.String(true, "") return strings.TrimSpace(s) } -func commandPaths(cmd *ffcli.Command, path string) []string { - root := fmt.Sprintf("%s%s", path, cmd.Name) - s := []string{root} - for _, sub := range cmd.Subcommands { - s = append(s, commandPaths(sub, root+"/")...) - } - sort.SliceStable(s, func(i, j int) bool { - return s[i] < s[j] - }) - return s -} - type tree map[string]tree -func (t tree) add(path string) { - t.addParts(strings.Split(path, "/")) -} - -func (t tree) addParts(parts []string) { - if len(parts) == 0 { - return +func commandTree(cmd *ffcli.Command) tree { + t := tree{} + for _, sub := range cmd.Subcommands { + t[sub.Name] = commandTree(sub) } - next, ok := t[parts[0]] - if !ok { - next = tree{} - t[parts[0]] = next - } - next.addParts(parts[1:]) + return t } func (t tree) keys() []string { @@ -61,8 +39,10 @@ func (t tree) String(root bool, padding string) string { index := 0 for _, k := range t.keys() { v := t[k] - s.WriteString(fmt.Sprintf("%s%s\n", padding+pipePad(root, pipe(index, len(t))), k)) - s.WriteString(v.String(false, padding+pipePad(root, outerPipe(index, len(t))))) + p := pipe(index, len(t)) + s.WriteString(fmt.Sprintf("%s%s\n", padding+pipePad(root, p), k)) + op := outerPipe(index, len(t)) + s.WriteString(v.String(false, padding+pipePad(root, op))) index++ } return s.String() @@ -70,10 +50,10 @@ func (t tree) String(root bool, padding string) string { func pipe(index int, len int) string { switch { - case index+1 == len: - return "└─" case index+1 > len: return " " + case index+1 == len: + return "└─" default: return "├─" }