Skip to main content

sva_cli/
help.rs

1// Concern: the `--help` page, each flag's default printed from its own constant | Non-concern: parsing those flags (args/), the JSON a subcommand answers (output.rs) | IO: () -> the page
2
3use sva_core::{
4    DEFAULT_LEDGER_DEPTH, DEFAULT_MAX_PEAKS, DEFAULT_OVERSAMPLE, DEFAULT_SILENT_BITS,
5    DEFAULT_SILENT_MAX_SECS,
6};
7use sva_engine::{DEFAULT_FRAME_SECS, DEFAULT_SAMPLE_RATE, PSYCHOACOUSTIC_V1};
8
9/// Read off the constants the parser itself defaults to, so a printed default cannot drift
10/// from the one a render actually uses.
11pub fn help_text() -> String {
12    let budget = PSYCHOACOUSTIC_V1.flop_budget;
13    format!(
14        r#"USAGE:
15  sva-cli (render | analyze | lint | trace | builtins | outline | new) [arguments]
16
17DESCRIPTION:
18  A composition is a directory of node files, each one closed-form expression in
19  `t` or `f`. sva-cli reads that composition and prints what it is and what it
20  sounds like, as JSON on stdout.
21
22  `--in <dir>` picks the composition for render, lint and trace, wherever in the
23  arguments it is written; without it they read the current directory. `new`
24  writes beside the current directory and `analyze` reads a file, so neither
25  takes `--in`.
26
27RENDER:
28  sva-cli render [<node|expression>] [query options]
29
30  Renders a node, `master` by default, and prints one reading per `--as`. The
31  argument may be an expression instead, in the grammar a node file's body uses.
32  Every reading states its `source` (exact or measured), the conformance profile
33  it ran under, and its rate.
34
35  `--as lines` and `--as atoms` read the closed form and allocate no buffer.
36  `--as samples=<path>.wav` writes 32-bit float audio, or 16-bit PCM under
37  `--pcm16`. Any other destination path takes the same JSON, uncapped. A path
38  that already holds a file refuses unless `--confirm` is written.
39
40  `--from`/`--to` bound the window a collapse runs over. `--sample-rate <hz>` is
41  the observation rate and is legal with every `--as`: no expression can read it.
42
43  `--to silent[:bits]` ends the render at its last sample at or over 2^-bits of
44  full scale, once a bound on every node proves no later sample reaches it.
45  Where silence is not proven by `--max <secs>` it refuses as
46  `engine.not_silent_by` with the bound there; a node that holds a level forever
47  refuses as `engine.never_silent`, and one no bound is derived for yet (a
48  physical solver other than chaigne_askenfelt, one string or a unison, a filter
49  whose coefficients move) as `engine.no_tail_bound`.
50  `--no-cache` skips the disk store. `cache.stats` lists every lookup the render
51  made of it, each a `hit` (with its `tier`) or computed, and stored or not.
52
53  `--as ledger` prints one row per node under the target. A row's `share` is the
54  part of its reader's own energy that row accounts for, so one reader's refs sum
55  to 1; a ref no addend isolates, such as one factor of a product, prints `null`.
56  Each ref carrying a share is collapsed once on its own, so a ledger costs one
57  collapse per attributed ref beyond the render, and `--depth` bounds how many.
58  `--brief` keeps only the rows that clipped, `--skim` drops the wider fields.
59
60  `--as arguments` renders nothing: for every instance under the target it
61  prints each builtin call's named arguments as the numbers the call was lowered
62  with, a solver's whole parameter set with `written: false` on each default it
63  filled in, and the operand each `min`/`max` `chosen` where a call folds one to a
64  number: in named arguments and a solver's, modal bank's or `noise`'s
65  positionals. One inside a filter's or cast's positional or `rand`'s key or
66  seed is not listed. `at` spans are bytes of the instance's
67  own body, as `sva-cli outline` counts them.
68
69ANALYZE:
70  sva-cli analyze <file.wav> [--as <representation>[=<destination>]]...
71
72  Runs the same readings over an external `.wav` at its own rate, never
73  resampled. Only the readings a buffer answers alone apply; the rest need the
74  graph behind it.
75
76LINT:
77  sva-cli lint [<node|expression>] [--in <dir>] [--format <json|text>]
78
79  Checks binding, ref and tempo resolution without rendering a sample. With no
80  target it checks the whole directory against `master`. With a target it checks
81  only the nodes that target reaches, and `entry-point` does not run, since the
82  target's own reach references every node in it.
83
84  Every check prints one `data.diagnostics` item. `advice` and `warning` exit 0,
85  `error` exits non-zero, so branch on the verdict and never on whether the array
86  is empty. No flag downgrades an error.
87
88  error    missing-comment       no `;` comment line
89           multiline-comment     more than one
90           malformed-comment     not four ` | ` fields, `Models:` `Neglects:`
91                                 `IO: <in> -> <out>` `Tags: <tag>[, <tag>...]`
92           long-comment-block    a `;` block over 1000 characters, the line-1
93                                 doc comment's own run exempted
94           long-expression-body  a body over 10000 characters, a backstop rather
95                                 than a complexity budget
96  warning  grid-rows-per-bar     a TSV grid's row count does not divide evenly
97                                 into its filename's bar span
98           key-is-not-a-pitch    `variables/key` holds neither a note name nor
99                                 a number of hertz
100           entry-point-refused   a node a whole-directory render reaches does
101                                 not type
102  advice   entry-point           nothing references this node
103           no-default-root       the directory has no `master`
104           tag-shape             a tag over 3 lowercase words or 24 characters
105           window-inside-ramp    a window sits wholly inside a crop's shoulder
106           literal-sample-rate   a written rate where `sp` belongs
107           not-a-file            a socket, FIFO or device in the directory
108           not-a-node            a filename no `@ref` can spell
109
110TRACE:
111  sva-cli trace <node|expression> [--in <dir>]
112
113  Prints one node's position without rendering audio: what it reads (`down`, one
114  hop), everything that reads it (`up`, transitively to an entry point), each
115  beside the expression doing the reading, the node that made it discrete, and
116  the feedback loop it sits in, if any.
117
118BUILTINS:
119  sva-cli builtins
120
121  Prints the whole callable and syntactic vocabulary: every builtin with its
122  arity and named arguments, each argument's `meaning`, `unit`, the model
123  `part` it sets and whether it `moves` with `t` (a filter's cutoff, q and gain;
124  every other named argument is one number, refused when it names none), each
125  positional's meaning where the model states one, unit suffixes, the note-name
126  grammar, reserved identifiers, special call shapes, and what has no operator
127  at all.
128
129OUTLINE:
130  sva-cli outline <expression>
131
132  Prints the parse tree the engine builds from one expression, each node with
133  the byte `span` it was written in: calls by `name` with positional and named
134  `args`, operators by `op`, refs by `path` with their `binds`, literals by
135  `value` and `unit`, names by `name`. A node the parser supplies itself, the
136  `0` of a prefix minus or the `t` of a bare `@ref`, has `written: false`.
137  Reads no composition.
138
139NEW:
140  sva-cli new <name> [--idempotency-key <key>]
141
142  Writes a starter composition at ./<name>, and refuses if that directory
143  exists. `--idempotency-key <key>` records the key beside the composition, so a
144  retry under the same key succeeds identically while the tree still holds what
145  was written. Any other key, or an edited tree, refuses.
146
147EXAMPLES:
148  sva-cli new song1 && cd song1
149  cd ./song1 && sva-cli render --as samples=/tmp/song1.wav
150  sva-cli render master --in ./song1 --as ledger --as loudness
151  cd ./song1 && sva-cli render chord/home --as lines
152  sva-cli lint --in ./song1
153  cd ./song1 && sva-cli lint voice/note
154  cd ./song1 && sva-cli trace grid/phrase-2b
155  sva-cli builtins
156
157OUTPUT:
158  {{"status": "success", "data": {{"node": "master", "down": {{"items": [...]}},
159  "diagnostics": {{"items": []}}}},
160   "meta": {{"request_id": "req_...", "timestamp": 1700000000}}}}
161
162  An error adds "error": {{"code", "message", "details": {{"count", "codes"}}}}.
163  Success or error, every response carries every finding in full at
164  "data": {{"diagnostics": {{"items": [{{"code", "severity", "message",
165  "location", "help"}}], "pagination": {{"count", "has_more", "next_cursor"}}}}}},
166  empty where it found none.
167
168  Every collection carries that same {{items, pagination}} pair. `count` is the
169  whole reading's, `has_more` says `items` holds less than that, and
170  `next_cursor` is a `--from` value to pass back verbatim for the rest. A framed
171  measurement restarts its state at that instant, so a second page is a second
172  reading rather than a continuation. `--as <name>=<path>` writes the whole
173  reading to a file instead, uncapped. This page is an envelope of its own, at
174  "data": {{"help"}}.
175
176DEFAULTS:
177  --depth <n>          how deep below its target a `ledger` walks.
178                       Default {DEFAULT_LEDGER_DEPTH}.
179  --peaks <n>          peaks a `spectrum` keeps, notes a `pitch`, formants a
180                       `formants`. Default {DEFAULT_MAX_PEAKS}.
181  --oversample <n>     the multiple `alias` re-renders at to hear what folded.
182                       Default {DEFAULT_OVERSAMPLE}.
183  --frame <secs>       the step a framed reading advances by, in seconds.
184                       Default {DEFAULT_FRAME_SECS}, except `spectrum`, which
185                       sizes its own transform to the window unless this flag is
186                       given.
187  --sample-rate <hz>   the observation rate a render lays its seconds on.
188                       Default {DEFAULT_SAMPLE_RATE}.
189  --flop-budget <n>    the operation count paid before a render refuses.
190                       Default {budget}, the `psychoacoustic-v1` profile's own.
191  --format <json|text> how `lint` prints its findings: the envelope, or one
192                       terminal line each, colored where stdout is a terminal.
193                       The same objects either way. Default json.
194  --in <dir>           the composition `render`, `lint` and `trace` read.
195                       Default: the directory the process runs in.
196  --node <path>        the instance a reading is taken of. Defaults to the
197                       target itself; `--as bindings` requires it.
198  --against <file.wav> the second signal `--as masking` reads against. No
199                       default: that one analysis requires it.
200  --from <time>        default 0s; `--to <time>` defaults to the node's extent.
201  --to silent[:bits]   bits default {DEFAULT_SILENT_BITS}, the profile's precision;
202                       `--max <secs>` defaults to {DEFAULT_SILENT_MAX_SECS}.
203  --confirm            replaces a destination that already holds a file. Without
204                       it a path already taken refuses as `conflict` and nothing
205                       is written.
206  --no-cache, --brief, --skim and --pcm16 are all off unless written.
207
208EXIT CODES:
209  0  success (error.code absent)
210  1  internal_error (a destination could not be written)
211  3  validation_error (bad arguments, or a composition that failed to parse)
212  4  conflict (a name `new` would overwrite, or a destination already holding a
213     file, without `--confirm`)
214  24 not_found (a `.wav` file, or a node this composition does not define)
215
216VERB ALIASES:
217  validate = lint, list = builtins, create = new, show = trace, from the `cli`
218  standard's own verb list. `render` and `analyze` take a reading, which that
219  list has no word for, so they keep their own names.
220
221SEE ALSO:
222  sva-cli --version    Show version information"#
223    )
224}