1// Copyright 2020 CUE Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package cmd
16
17import (
18	"github.com/spf13/cobra"
19)
20
21// TODO: intersperse the examples at the end of the texts in the
22// body of text to make things more concrete for the user early on?
23// The current approach works will if users just print the text without
24// "more" or "less", in which case the examples show more prominently.
25// The user can then scroll up to get a more in-depth explanation. But is
26// this how users use it?
27
28func newHelpTopics(c *Command) []*cobra.Command {
29	return []*cobra.Command{
30		inputsHelp,
31		flagsHelp,
32		filetypeHelp,
33		injectHelp,
34		commandsHelp,
35	}
36}
37
38var inputsHelp = &cobra.Command{
39	Use:   "inputs",
40	Short: "package list, patterns, and files",
41	Long: `Many commands apply to a set of inputs:
42
43cue <command> [inputs]
44
45The list [inputs] may specify CUE packages, CUE files, non-CUE
46files or some combinations of those. An empty list specifies
47the package in the current directory, provided there is a single
48named package in this directory.
49
50CUE packages are specified as an import path. An import path
51that is a rooted path --one that begins with a "." or ".."
52element-- is interpreted as a file system path and denotes the
53package instance in that directory.
54
55Otherwise, the import path P denotes and external package found
56in cue.mod/{pkg|gen|usr}/P.
57
58An import path may contain one or more "..." to match any
59subdirectory: pkg/... matches all packages below pkg, including
60pkg itself, while foo/.../bar matches all directories named bar
61within foo. In all cases, directories containing cue.mod
62directories are excluded from the result.
63
64A package may also be specified as a list of .cue files.
65The special symbol '-' denotes stdin or stdout and defaults to
66the cue file type for stdin. For stdout, the default depends on
67the cue command. A .cue file package may not be combined with
68regular packages.
69
70Non-cue files are interpreted based on their file extension or,
71if present, an explicit file qualifier (see the "filetypes"
72help topic). By default, all recognized files are unified at
73their root value. See the "filetypes" and "flags" help topics
74on how to treat each file individually or how to combine them
75differently.
76
77If a data file has multiple values, such as allowed with JSON
78Lines or YAML, each value is interpreted as a separate file.
79
80If the --schema/-d is specified, data files are not merged, and
81are compared against the specified schema within a package or
82non-data file. For OpenAPI, the -d flag specifies a schema name.
83For JSON Schema the -d flag specifies a schema defined in
84"definitions". In all other cases, the -d flag is a CUE
85expression that is evaluated within the package.
86
87Examples (also see also "flags" and "filetypes" help topics):
88
89# Show the definition of each package named foo for each
90# directory dir under path.
91$ cue def ./path/.../dir:foo
92
93# Unify each document in foo.yaml with the value Foo in pkg.
94$ cue export ./pkg -d Foo foo.yaml
95
96# Unify data.json with schema.json.
97$ cue export data.json schema: schema.json
98`,
99}
100
101var flagsHelp = &cobra.Command{
102	Use:   "flags",
103	Short: "common flags for composing packages",
104	Long: `Non-CUE files are merged at their roots by default.
105The can be combined differently or treated as different files
106by using a combination of the following flags.
107
108
109Individual files
110
111To treat non-cue files as individual files, use --no-merge flag.
112This is the default for vet. This flag only applies to data files
113when used in combination with the --schema/-d flag.
114
115
116Assigning values to a CUE path
117
118The --path/-l flag can be used to specify a CUE path at which to
119place a value. Each -l flag specifies either a CUE expression or
120a CUE field (without the value following the colon), both of
121which are evaluated within the value. Together, the -l flags
122specify the path at increasingly deeper nesting. In the path
123notation, path elements that end with a "::", instead of ":",
124are created as definitions. An expression may refer to builtin
125packages as long as the name can be uniquely identified.
126
127The --with-context flag can be used to evaluate the label
128expression within a struct of contextual data, instead of
129within the value itself. This struct has the following fields:
130
131{
132	// data holds the original source data
133	// (perhaps one of several records in a file).
134	data: _
135	// filename holds the full path to the file.
136	filename: string
137	// index holds the 0-based index element of the
138	// record within the file. For files containing only
139	// one record, this will be 0.
140	index: uint & <recordCount
141	// recordCount holds the total number of records
142	// within the file.
143	recordCount: int & >=1
144}
145
146
147Handling multiple documents or streams
148
149To handle multi-document files, such as JSON Lines or YAML
150files with document separators (---), the user must specify
151a the --path, --list, or --files flag.
152The --path flag merges each element into a single package as
153if each element was defined in a separate file. The --list flag
154concatenates each entry in a file into a list.
155Using --list flag in combination with the --path flag
156concatenates entries with the same path into a list, instead of
157unifying them.
158Finally, the --files option causes each entry to be written to
159a different file. The -files flag may only be used in
160combination with the import command.
161
162
163Examples:
164
165# Put a value at a path based on its "kind" and "name" fields.
166$ cue eval -l 'strings.ToLower(kind)' -l name foo.yaml
167
168# Include a schema under the "myschema" field using the path notation.
169$ cue eval -l myschema: schema: foo.json
170
171# Base the path values on its kind and file name.
172$ cue eval --with-context -l 'path.Base(filename)' -l data.kind foo.yaml
173`,
174}
175
176var filetypeHelp = &cobra.Command{
177	Use:   "filetypes",
178	Short: "supported file types and qualifiers",
179	Long: `The cue tools supports the following file types:
180
181    Tag         Extensions      Description
182    cue         .cue            CUE source files.
183    json        .json           JSON files.
184    yaml        .yaml/.yml      YAML files.
185    jsonl       .jsonl/.ldjson  Line-separated JSON values.
186    jsonschema                  JSON Schema.
187    openapi                     OpenAPI schema.
188	pb                          Use Protobuf mappings (e.g. json+pb)
189    textproto    .textproto     Text-based protocol buffers.
190    proto        .proto         Protocol Buffer definitions.
191    go           .go            Go source files.
192    text         .txt           Raw text file; the evaluated value
193                                must be of type string.
194    binary                      Raw binary file; the evaluated value
195                                must be of type string or bytes.
196
197OpenAPI, JSON Schema and Protocol Buffer definitions are
198always interpreted as schema. YAML and JSON are always
199interpreted as data. CUE and Go are interpreted as schema by
200default, but may be selected to operate in data mode.
201
202The cue tool will infer a file's type from its extension by
203default. The user my override this behavior by using qualifiers.
204A qualifier takes the form
205
206    <tag>{'+'<tag>}':'
207
208For instance,
209
210	cue eval json: foo.data
211
212specifies that 'foo.data' should be read as a JSON file. File
213formats that do not have a default extension may be represented
214in any data format using the same notation:
215
216   cue def jsonschema: bar.cue foo.yaml openapi+yaml: baz.def
217
218interprets the files bar.cue and foo.yaml as data in the
219respective formats encoding an JSON Schema, while 'baz.def' is
220defined to be a YAML file which contents encode OpenAPI
221definitions.
222
223A qualifier applies to all files following it on the command line
224until the next qualifier. The cue tool does not allow a ':' in
225filenames.
226
227The following tags can be used in qualifiers to further
228influence input or output. For input these act as
229restrictions, validating the input. For output these act
230as filters, showing only the requested data and picking
231defaults as requested.
232
233    Tag         Description
234    data        Require concrete input and output that does
235                not require any evaluation.
236    graph       Like data, but allow references.
237    schema      Export data and definitions.
238
239Many commands also support the --out and --outfile/-o flags.
240The --out flag specifies the output type using a qualifier
241(without the ':'). The -o flag specifies an output file
242possibly prefixed with a qualifier.
243
244Examples:
245
246# Interpret bar.cue and foo.yaml as OpenAPI data.
247$ cue def openapi: bar.cue foo.yaml
248
249# Write a CUE package as OpenAPI encoded as YAML, using
250# an alternate file extension.
251$ cue def -o openapi+yaml:foo.openapi
252
253# Print the data for the current package as YAML.
254$ cue export --out=yaml
255
256# Print the string value of the "name" field as a string.
257$ cue export -e name --out=text
258
259# Write the string value of the "name" field to a text file.
260$ cue export -e name -o=foo.txt
261
262# Write the string value of the "name" field to a file foo.
263$ cue export -e name -o=text:foo
264`,
265}
266
267var injectHelp = &cobra.Command{
268	Use:   "injection",
269	Short: "inject files or values into specific fields for a build",
270	Long: `Many of the cue commands allow injecting values or
271selecting files from the command line using the --inject/-t flag.
272
273
274Injecting files
275
276A "build" attribute defines a boolean expression that causes a file
277to only be included in a build if its expression evaluates to true.
278There may only be a single @if attribute per file and it must
279appear before a package clause.
280
281The expression is a subset of CUE consisting only of identifiers
282and the operators &&, ||, !, where identifiers refer to tags
283defined by the user on the command line.
284
285For example, the following file will only be included in a build
286if the user includes the flag "-t prod" on the command line.
287
288   // File prod.cue
289   @if(prod)
290
291   package foo
292
293
294Injecting values
295
296The injection mechanism allows values to be injected into fields
297that are not defined within the scope of a comprehension, list, or
298optional field and that are marked with a "tag" attribute. For any
299field of the form
300
301   field: x @tag(key)
302
303an "--inject key=value" flag will modify the field to
304
305   field: x & "value"
306
307By default, the injected value is treated as a string.
308Alternatively, the "type" option allows a value to be interpreted
309as an int, number, or bool. For instance, for a field
310
311   field: x @tag(key,type=int)
312
313the flag "-t key=2" modifies the field to
314
315   field: x & 2
316
317Valid values for type are "int", "number", "bool", and "string".
318
319A tag attribute can also define shorthand values, which can be
320injected into the fields without having to specify the key. For
321instance, for
322
323   environment: string @tag(env,short=prod|staging)
324
325"-t prod" sets the environment field to the value "prod". It is
326still possible to specify "-t env=prod" in this case.
327
328Use the usual CUE constraints to limit the possible values of a
329field. For instance
330
331   environment: "prod" | "staging" @tag(env,short=prod|staging)
332
333ensures the user may only specify "prod" or "staging".
334
335
336Tag variables
337
338The injection mechanism allows for the injection of system variables:
339when variable injection is enabled, tags of the form
340
341    @tag(dir,var=cwd)
342
343will inject the named variable (here cwd) into the tag. An explicitly
344set value for a tag using --inject/-t takes precedence over an
345available tag variable.
346
347The following variables are supported:
348
349   now        current time in RFC3339 format.
350   os         OS identifier of the current system. Valid values:
351                aix       android   darwin    dragonfly
352                freebsd   illumos   ios       js (wasm)
353                linux     netbsd    openbsd   plan9
354                solaris   windows
355   cwd        working directory
356   username   current username
357   hostname   current hostname
358   rand       a random 128-bit integer
359`,
360}
361
362var commandsHelp = &cobra.Command{
363	Use:   "commands",
364	Short: "user-defined commands",
365	Long: `Commands define actions on instances. For example, they may
366specify how to upload a configuration to Kubernetes. Commands are
367defined directly in tool files, which are regular CUE files
368within the same package with a filename ending in _tool.cue.
369These are typically defined at the module root so that they apply
370to all instances.
371
372Each command consists of one or more tasks. A task may, for
373example, load or write a file, consult a user on the command
374line, fetch a web page, and so on. Each task has inputs and
375outputs. Outputs are typically filled out by the task
376implementation as the task completes.
377
378Inputs of tasks my refer to outputs of other tasks. The cue tool
379does a static analysis of the configuration and only starts tasks
380that are fully specified. Upon completion of each task, cue
381rewrites the instance, filling in the completed task, and
382reevaluates which other tasks can now start, and so on until all
383tasks have completed.
384
385Available tasks can be found in the package documentation at
386
387	https://pkg.go.dev/cuelang.org/go/pkg/tool?tab=subdirectories
388
389More on tasks can be found in the commands help topic.
390
391Examples:
392
393In this simple example, we define a command called "hello",
394which declares a single task called "print" which uses
395"tool/exec.Run" to execute a shell command that echos output to
396the terminal:
397
398	$ cat <<EOF > hello_tool.cue
399	package foo
400
401	import "tool/exec"
402
403	city: "Amsterdam"
404	who: *"World" | string @tag(who)
405
406	// Say hello!
407	command: hello: {
408		print: exec.Run & {
409			cmd: "echo Hello \(who)! Welcome to \(city)."
410		}
411	}
412	EOF
413
414We run the "hello" command like this:
415
416	$ cue cmd hello
417	Hello World! Welcome to Amsterdam.
418
419	$ cue cmd --inject who=Jan hello
420	Hello Jan! Welcome to Amsterdam.
421
422
423In this example we declare the "prompted" command which has four
424tasks. The first task prompts the user for a string input. The
425second task depends on the first, and echos the response back to
426the user with a friendly message. The third task pipes the output
427from the second to a file. The fourth task pipes the output from
428the second to standard output (i.e. it echos it again).
429
430	package foo
431
432	import (
433		"tool/cli"
434		"tool/exec"
435		"tool/file"
436	)
437
438	city: "Amsterdam"
439
440	// Say hello!
441	command: prompter: {
442		// save transcript to this file
443		var: file: *"out.txt" | string @tag(file)
444
445		ask: cli.Ask & {
446			prompt:   "What is your name?"
447			response: string
448		}
449
450		// starts after ask
451		echo: exec.Run & {
452			cmd:    ["echo", "Hello", ask.response + "!"]
453			stdout: string // capture stdout
454		}
455
456		// starts after echo
457		append: file.Append & {
458			filename: var.file
459			contents: echo.stdout
460		}
461
462		// also starts after echo
463		print: cli.Print & {
464			text: echo.stdout
465		}
466	}
467
468The types of the commands and tasks are defined in CUE itself at
469cuelang.org/go/pkg/tool/tool.cue.
470
471	command: [Name]: Command
472
473	Command: {
474		// Tasks specifies the things to run to complete a command. Tasks are
475		// typically underspecified and completed by the particular internal
476		// handler that is running them. Tasks can be a single task, or a full
477		// hierarchy of tasks.
478		//
479		// Tasks that depend on the output of other tasks are run after such tasks.
480		// Use $after if a task needs to run after another task but does not
481		// otherwise depend on its output.
482		Tasks
483
484		//
485		// Example:
486		//     mycmd [-n] names
487		$usage?: string
488
489		// short is short description of what the command does.
490		$short?: string
491
492		// long is a longer description that spans multiple lines and
493		// likely contain examples of usage of the command.
494		$long?: string
495	}
496
497	// Tasks defines a hierarchy of tasks. A command completes if all
498	// tasks have run to completion.
499	Tasks: Task | {
500		[name=Name]: Tasks
501	}
502
503	// Name defines a valid task or command name.
504	Name: =~#"^\PL([-](\PL|\PN))*$"#
505
506	// A Task defines a step in the execution of a command.
507	Task: {
508		$type: "tool.Task" // legacy field 'kind' still supported for now.
509
510		// kind indicates the operation to run. It must be of the form
511		// packagePath.Operation.
512		$id: =~#"\."#
513
514		// $after can be used to specify a task is run after another one, when
515		// it does not otherwise refer to an output of that task.
516		$after?: Task | [...Task]
517	}
518`,
519}
520
521// TODO: tags
522// - doc/nodoc
523// - attr/noattr
524// - id=<url>
525
526// TODO: filetypes:
527// - binpb
528
529// TODO: cue.mod help topic
530