1
1
package commands
2
2
3
3
import (
4
+ "bytes"
5
+ "fmt"
6
+ "io"
4
7
"os"
8
+ "path/filepath"
9
+ "sort"
10
+ "strings"
5
11
6
12
"github.com/hasura/graphql-engine/cli"
13
+ "github.com/hasura/graphql-engine/cli/assets"
7
14
"github.com/pkg/errors"
8
15
"github.com/spf13/cobra"
9
16
"github.com/spf13/cobra/doc"
@@ -32,7 +39,7 @@ func NewDocsCmd(ec *cli.ExecutionContext) *cobra.Command {
32
39
case "md" :
33
40
err = doc .GenMarkdownTree (rootCmd , docDirectory )
34
41
case "rest" :
35
- err = doc . GenReSTTree (rootCmd , docDirectory )
42
+ err = genReSTTreeCustom (rootCmd , docDirectory , "Hasura CLI: " , func ( s string ) string { return "" }, sphinxLinkHandler )
36
43
case "yaml" :
37
44
err = doc .GenYamlTree (rootCmd , docDirectory )
38
45
default :
@@ -51,3 +58,210 @@ func NewDocsCmd(ec *cli.ExecutionContext) *cobra.Command {
51
58
f .StringVar (& docDirectory , "directory" , "docs" , "directory where docs should be generated" )
52
59
return docsCmd
53
60
}
61
+
62
+ func sphinxLinkHandler (name , ref string ) string {
63
+ return fmt .Sprintf (":ref:`%s <%s>`" , name , ref )
64
+ }
65
+
66
+ // taken from https://github.com/spf13/cobra/blob/master/doc/rest_docs.go
67
+ func printOptionsReST (buf * bytes.Buffer , cmd * cobra.Command , name string ) error {
68
+ flags := cmd .NonInheritedFlags ()
69
+ flags .SetOutput (buf )
70
+ if flags .HasFlags () {
71
+ buf .WriteString ("Options\n " )
72
+ buf .WriteString ("~~~~~~~\n \n ::\n \n " )
73
+ flags .PrintDefaults ()
74
+ buf .WriteString ("\n " )
75
+ }
76
+
77
+ parentFlags := cmd .InheritedFlags ()
78
+ parentFlags .SetOutput (buf )
79
+ if parentFlags .HasFlags () {
80
+ buf .WriteString ("Options inherited from parent commands\n " )
81
+ buf .WriteString ("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n \n ::\n \n " )
82
+ parentFlags .PrintDefaults ()
83
+ buf .WriteString ("\n " )
84
+ }
85
+ return nil
86
+ }
87
+
88
+ // linkHandler for default ReST hyperlink markup
89
+ func defaultLinkHandler (name , ref string ) string {
90
+ return fmt .Sprintf ("`%s <%s.rst>`_" , name , ref )
91
+ }
92
+
93
+ // genReST creates reStructured Text output.
94
+ func genReST (cmd * cobra.Command , w io.Writer , titlePrefix string ) error {
95
+ return genReSTCustom (cmd , w , titlePrefix , defaultLinkHandler )
96
+ }
97
+
98
+ // genReSTCustom creates custom reStructured Text output.
99
+ func genReSTCustom (cmd * cobra.Command , w io.Writer , titlePrefix string , linkHandler func (string , string ) string ) error {
100
+ cmd .InitDefaultHelpCmd ()
101
+ cmd .InitDefaultHelpFlag ()
102
+
103
+ buf := new (bytes.Buffer )
104
+ name := cmd .CommandPath ()
105
+ ref := strings .Replace (name , " " , "_" , - 1 )
106
+ cliDocPath := "manifests/docs/" + ref + ".rst"
107
+ short := cmd .Short
108
+ long := cmd .Long
109
+ if len (long ) == 0 {
110
+ long = short
111
+ }
112
+ fileInfo , er := assets .Asset (cliDocPath )
113
+ var info string
114
+ if er != nil || string (fileInfo ) == "" {
115
+ info = short
116
+ } else {
117
+ info = string (fileInfo )
118
+ }
119
+
120
+ buf .WriteString (".. _" + ref + ":\n \n " )
121
+
122
+ buf .WriteString (titlePrefix + name + "\n " )
123
+ buf .WriteString (strings .Repeat ("-" , len (titlePrefix + name )) + "\n \n " )
124
+ buf .WriteString (info + "\n \n " )
125
+
126
+ buf .WriteString ("Synopsis\n " )
127
+ buf .WriteString ("~~~~~~~~\n \n " )
128
+ buf .WriteString ("\n " + long + "\n \n " )
129
+
130
+ if cmd .Runnable () {
131
+ buf .WriteString (fmt .Sprintf ("::\n \n %s\n \n " , cmd .UseLine ()))
132
+ }
133
+
134
+ if len (cmd .Aliases ) > 0 {
135
+ buf .WriteString ("Alias: " + strings .Join (cmd .Aliases , ", " ) + "\n \n " )
136
+ }
137
+
138
+ if len (cmd .Example ) > 0 {
139
+ buf .WriteString ("Examples\n " )
140
+ buf .WriteString ("~~~~~~~~\n \n " )
141
+ buf .WriteString (fmt .Sprintf ("::\n \n %s\n \n " , indentString (cmd .Example , " " )))
142
+ }
143
+
144
+ if err := printOptionsReST (buf , cmd , name ); err != nil {
145
+ return err
146
+ }
147
+ if hasSeeAlso (cmd ) {
148
+ buf .WriteString ("SEE ALSO\n " )
149
+ buf .WriteString ("~~~~~~~~\n \n " )
150
+ if cmd .HasParent () {
151
+ parent := cmd .Parent ()
152
+ pname := parent .CommandPath ()
153
+ ref = strings .Replace (pname , " " , "_" , - 1 )
154
+ buf .WriteString (fmt .Sprintf ("* %s \t - %s\n " , linkHandler (pname , ref ), parent .Short ))
155
+ cmd .VisitParents (func (c * cobra.Command ) {
156
+ if c .DisableAutoGenTag {
157
+ cmd .DisableAutoGenTag = c .DisableAutoGenTag
158
+ }
159
+ })
160
+ }
161
+
162
+ children := cmd .Commands ()
163
+ sort .Sort (byName (children ))
164
+
165
+ for _ , child := range children {
166
+ if ! child .IsAvailableCommand () || child .IsAdditionalHelpTopicCommand () {
167
+ continue
168
+ }
169
+ cname := name + " " + child .Name ()
170
+ ref = strings .Replace (cname , " " , "_" , - 1 )
171
+ buf .WriteString (fmt .Sprintf ("* %s \t - %s\n " , linkHandler (cname , ref ), child .Short ))
172
+ }
173
+ buf .WriteString ("\n " )
174
+ }
175
+ if ! cmd .DisableAutoGenTag {
176
+ buf .WriteString ("*Auto generated by spf13/cobra*\n " )
177
+ }
178
+ _ , err := buf .WriteTo (w )
179
+ return err
180
+ }
181
+
182
+ // genReSTTree will generate a ReST page for this command and all
183
+ // descendants in the directory given.
184
+ // This function may not work correctly if your command names have `-` in them.
185
+ // If you have `cmd` with two subcmds, `sub` and `sub-third`,
186
+ // and `sub` has a subcommand called `third`, it is undefined which
187
+ // help output will be in the file `cmd-sub-third.1`.
188
+ func genReSTTree (cmd * cobra.Command , dir , titlePrefix string ) error {
189
+ emptyStr := func (s string ) string { return "" }
190
+ return genReSTTreeCustom (cmd , dir , titlePrefix , emptyStr , defaultLinkHandler )
191
+ }
192
+
193
+ // genReSTTreeCustom is the the same as genReSTTree, but
194
+ // with custom filePrepender and linkHandler.
195
+ func genReSTTreeCustom (cmd * cobra.Command , dir , titlePrefix string , filePrepender func (string ) string , linkHandler func (string , string ) string ) error {
196
+ for _ , c := range cmd .Commands () {
197
+ if ! c .IsAvailableCommand () || c .IsAdditionalHelpTopicCommand () {
198
+ continue
199
+ }
200
+ if err := genReSTTreeCustom (c , dir , titlePrefix , filePrepender , linkHandler ); err != nil {
201
+ return err
202
+ }
203
+ }
204
+
205
+ basename := strings .Replace (cmd .CommandPath (), " " , "_" , - 1 ) + ".rst"
206
+ filename := filepath .Join (dir , basename )
207
+ f , err := os .Create (filename )
208
+ if err != nil {
209
+ return err
210
+ }
211
+ defer f .Close ()
212
+
213
+ if _ , err := io .WriteString (f , filePrepender (filename )); err != nil {
214
+ return err
215
+ }
216
+ if err := genReSTCustom (cmd , f , titlePrefix , linkHandler ); err != nil {
217
+ return err
218
+ }
219
+ return nil
220
+ }
221
+
222
+ // adapted from: https://github.com/kr/text/blob/main/indent.go
223
+ func indentString (s , p string ) string {
224
+ var res []byte
225
+ b := []byte (s )
226
+ prefix := []byte (p )
227
+ bol := true
228
+ for _ , c := range b {
229
+ if bol && c != '\n' {
230
+ res = append (res , prefix ... )
231
+ }
232
+ res = append (res , c )
233
+ bol = c == '\n'
234
+ }
235
+ return string (res )
236
+ }
237
+
238
+ // Test to see if we have a reason to print See Also information in docs
239
+ // Basically this is a test for a parent commend or a subcommand which is
240
+ // both not deprecated and not the autogenerated help command.
241
+ func hasSeeAlso (cmd * cobra.Command ) bool {
242
+ if cmd .HasParent () {
243
+ return true
244
+ }
245
+ for _ , c := range cmd .Commands () {
246
+ if ! c .IsAvailableCommand () || c .IsAdditionalHelpTopicCommand () {
247
+ continue
248
+ }
249
+ return true
250
+ }
251
+ return false
252
+ }
253
+
254
+ // Temporary workaround for yaml lib generating incorrect yaml with long strings
255
+ // that do not contain \n.
256
+ func forceMultiLine (s string ) string {
257
+ if len (s ) > 60 && ! strings .Contains (s , "\n " ) {
258
+ s = s + "\n "
259
+ }
260
+ return s
261
+ }
262
+
263
+ type byName []* cobra.Command
264
+
265
+ func (s byName ) Len () int { return len (s ) }
266
+ func (s byName ) Swap (i , j int ) { s [i ], s [j ] = s [j ], s [i ] }
267
+ func (s byName ) Less (i , j int ) bool { return s [i ].Name () < s [j ].Name () }
0 commit comments