#The hd Command
This page is the task-oriented guide to the hd command. The normative
reference is Command Line; this page shows
each job the way you would do it, with real output.
#Start A Project
You want a new package. hd new needs to know which kind: --app for an
application, --lib for a library:
$ hd new orders --app
created application package `orders` in /home/you/orders
The layout is small: an hd.toml manifest, src/main.hd for the entry
point, and tests/ for integration tests:
orders/hd.toml
orders/src/main.hd
orders/tests/orders.hd
The manifest names the package; everything else is convention:
[package]
name = "orders"
Read next: Package Mode.
#Run A Program
You want to see it run. Inside a package, hd run runs its executable;
hd FILE runs one file anywhere:
$ hd run
hello, world
With several executables or a task, name it: hd run NAME runs the
executable or the tasks/NAME.hd script. hd run --release runs the
release build. In a workspace, -p NAME selects the member, from the root
or inside another member:
$ hd run seed # runs tasks/seed.hd
seeded
Read next: Running A Package and Tasks.
An implementation may offer more than one engine to run programs on.
hd FILE, hd run, hd build and hd test take --engine NAME, and
HD_ENGINE gives the name when the flag is absent. Which engines exist,
and which is the default, depends on your hd. A program behaves the same
on each; only speed and memory use differ. One difference is security:
the wasmtime engine opens files relative to each granted directory, so
a grant holds even if another process swaps a link inside it while the
program runs. The node engine checks a path and then opens it by name,
so a local process that swaps a link between the two can reach outside the
grant. Run untrusted programs on wasmtime. A name your hd does not
offer is a usage error that exits 101 and runs nothing:
$ hd run --engine no-such-name
$ echo $?
101
Read next: Engines.
#Parse Your Program's Arguments
std.cli.ArgParser declares your program's options and provides -h and
--help. Return error.exit() from main to use its command-line policy:
help goes to standard output with status 0, and usage errors go to standard
error with status 2.
Save this as volume.hd:
use std.cli.{Arg, ArgParser}
use std.host.Args
use std.process.ExitCode
pub fn main() -> ExitCode $ Args + Console:
parser := ArgParser::new("volume").arg(Arg { name: "level", help: "how loud" })
parsed := match parser.parse_args():
.Ok(parsed) => parsed
.Err(error) => return error.exit()
println(parsed.value("level").unwrap_or("quiet"))
ExitCode(0)Open in playgroundPass its arguments after --:
$ hd volume.hd -- --level 3
3
$ hd volume.hd -- --levl 3
unknown option --levl
$ echo $?
2
$ hd volume.hd -- --help >help.txt
$ echo $?
0
help.txt contains the generated usage. The exit policy belongs to
CliError, and main selects it by returning error.exit(). Propagating a
parse error with ? instead uses the ordinary Result entry behavior.
Read next: Argument Parser and Exit Status.
#Check And Build
You want to know it compiles, without running it. hd check type-checks
the package, and hd build writes the Wasm:
$ hd check
check result: ok. errors: 0; warnings: 0
$ hd build
$ hd build --release
The outputs land in build/debug/ and build/release/:
build/debug/orders.wasm
build/release/orders.wasm
Read next: Building And Checking.
#Test
You want the tests. hd test runs every test of the package; --filter
runs only the cases whose name contains a word:
$ hd test
test result: ok. 1 passed; 0 failed; 0 ignored
$ hd test --filter "prints"
test result: ok. 1 passed; 0 failed; 0 ignored
A unit test runs on fakes alone; an integration test under tests/ gets
the real system and can run the built program with hd_run!. A doc test
is a fenced hd block in a ## doc comment. A snapshot test compares
with a recorded file, and hd test --update records it. For agents, hd test --format json prints one JSON line per case plus a summary:
$ hd test --format json
{"kind":"test","name":"prints a greeting","outcome":"passed","message":""}
{"kind":"summary","errors":0,"warnings":0,"passed":1,"failed":0,"ignored":0,"status":0}
You edit one module and want feedback in seconds, not the whole suite.
After a full pass, hd records each test program's fingerprint: its
sources, dependencies, manifest settings, and hd version. hd test --affected then runs only the programs whose fingerprint changed, and the
summary counts the rest:
$ hd test --affected
test result: ok. 0 passed; 0 failed; 0 ignored; 1 program skipped
A filtered run or a failing program records nothing, so --affected never
skips a test that failed or did not run. hd clean removes the records.
A runaway loop should fail, not hang. --time-limit gives each test case,
and hd run and hd FILE give the program, a deadline; past it the
program panics with time-limit:
$ hd test --time-limit 5s
$ hd run --time-limit 30s
panic: time-limit: the program exceeded its time limit
A Ctrl-C asks a waiting program to stop and lets its cleanup finish:
using disposes and defer suites run, for up to ten seconds, then
whatever is left is abandoned. A second Ctrl-C abandons it at once.
--cleanup-limit changes the wait, and the run exits with status 130:
$ hd run --cleanup-limit 2s
^C
cancelled
Read next: Test Runs, Affected Tests, Program Limits, and Cancelling A Run.
#Dependencies And Workspaces
You want another package's code. A requirement is a minimum version of a host path, or a local path while you develop:
[dependencies]
json = "github.com/acme/json@2.1.0"
billing = { path = "../billing" }
hd add, hd update, hd remove, and hd fetch manage the table and
hd.sum, the recorded hashes beside the manifest. Several packages of one
repository share one selection through a workspace manifest:
[workspace]
members = ["libs/ui", "libs/shared"]
Read next: Dependency Commands and Workspaces.
#Debug And Explore
You want to see a value while it runs, or read what a function promises.
dbg(x) prints the value with its source location on standard error.
hd doc NAME prints one item's documentation, and no file is written:
$ hd doc std.text.split
```hd
pub fn split(self, separator: string) -> List[string]
```
Splits the string around each occurrence of `separator`, in order.
Reference: std.text.string.split in the standard library reference
NAME is a module path and an item, as in text.slugify for your own
package, or std.text.split for the standard library, where the output
is the signature, a summary, and a reference link. A module name
alone, as in hd doc std.text, prints the module's page. hd help COMMAND prints one command's usage and flags.
hd with no arguments on a terminal opens the interactive session; each
line is evaluated and printed with its type. Text piped into hd runs as
a program instead, so a session cannot be scripted through standard input.
A later input may use the top-level bindings and declarations of earlier
ones, but may not redeclare a name an earlier input declared
(duplicate-binding, duplicate-type); to change a declaration, start a
new session or give the new one another name. An input that panics adds
nothing to the session, and the session goes on. An input that starts with
: is a command: :help lists them, :quit (or :q, :exit) ends the
session, and :reload reads the package's files again after you edit
them, keeping the inputs that still check.
$ hd
1 + 2
3 : usize
Every diagnostic command takes --format json for one JSON object per
diagnostic. Read next:
Machine Output.
#Common Errors And Their Fixes
missing-requirement: the call needs a capability, such asConsole, that the enclosing signature does not grant. Add it to the row, as inpub fn main() -> void $ Console, or bind a provider in a test.invalid-result-propagation:?needs the function's own result type to be a compatibleResult; the note names the type to write.non-reassignable-binding::=bindings are fixed; writeletfor a name you reassign.type-mismatchonusizeversusi32: literals default tousize; write+Nfor a signed one.mixed-signedness: an unsignedusize, whichlen()and literals give, does not mix with a signedi32. Declare the configurable countusize, or cast one side withusize(n)ori32(n).bang-call-outside-suspension: the call suspends, so its caller must be a suspending function:fn!, ormain!.
Each message names its code, and the rule IDs it cites are in the specification.