Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions design/mvp/CLIEmbedding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Typed Main
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Typed Main
# Command-Line Interface Embedding


A high-level goal of the component model is the ability to run a single component portably on various hosts. One such embedding is a classic Unix-style Command-Line Interface (CLI) accepting positional/named arguments.

This document describes how a component can be generically interpreted by a component-enabled CLI to allow components to be directly instantiated and invoked from the command line in an idiomatic manner, corresponding to use case #2 of [invoking component exports](https://github.com/WebAssembly/component-model/blob/main/design/high-level/UseCases.md#invoking-component-exports-from-the-host). It also defines annotations which can be used to extend the default interpretation of a component to specify CLI-specific features (e.g. short flag names).

The component type is specified here in terms of a [`*.wit`](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md) interface and annotations as [Structured Annotations](https://github.com/WebAssembly/component-model/issues/58).

## Arguments
* Value imports are can be supplied as positional arguments in the order they are defined
* Value imports can be supplied as long-form named arguments with their defined name
* The ordering of named arguments with respect to each other and positional arguments is not constrained
* If a named argument is present, parsing of positional argument skips over it to the next position

```rust
input: string
pattern: string
replace: string
```
Comment on lines +15 to +19
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once @fibonacci1729 posts a sketch .world file syntax (soon, I think), it'd be very nice to update the syntax in all these wit blocks, which would have the nice effect of being clear what's an import and export.


### Usage
```
$ replace "some-thing" "-" "_"
$ replace --input "some-thing" --pattern "-" --replace "_"
$ replace --pattern "-" --replace "_" "some-thing"
```

## File Preopen Arguments
* If the type of a value import is a WASI file, the host reads the corresponding argument as a path and preopens it.
* The preopen must occur even for file paths which do not exist (as long as their parent does), so that output file paths can be supported.

```rust
import { file } from ...

input: file
output: file
```

### Usage
```bash
$ copy ./input.txt --output ./output.txt
```

## Optional Arguments
* Value imports with type `option<T>` are optional arguments
* If an optional argument is not provided, the value import takes value `none`
* All arguments defined after an optional argument must be optional

```rust
import { file } from ...

input: file
output: option<file>
```

### Usage
```bash
$ foo ./input.txt ./output.txt
$ foo ./input.txt
```

## Flags
* Value imports with type `bool` inferred to be flags.
* Flags have value `true` if present and `false` if absent.
* By default, flags are "long"
* Equivalent to annotation `@flag(long)`
* Specified in long form with no associated value e.g. `--foobar`
* Name is inferred to be the value import name
* Name can be explicitly set `@flag(long = "foobar")`
* Does not support grouping
* Flags can annotated as "short"
* Using annotation `@flag(short)`
* Specified in short form with no associated value e.g.`-f`
* Name is inferred to be first character of value import name
* Name can be explicitly set `@flag(short = "f")`
* Supports grouping `-a -b -c` = `-abc`
* Flag can be short and long `@flag(short, long)`, value `true` if either are present
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me like the long name should always be accepted (since it's explicitly part of the component interface as an import) and that the @flag(short) annotation only adds an additional syntactic sugar option. One can imagine a scenario where I use some general component tooling (say, a component discovery tool which doesn't know about the CLI structured annotation) to look at the signature of the component and I see a value import named foo so I'm unpleasantly surprised when I try to pass --foo on the CLI and it's rejected. (And then if we agree on that, I think there's no need for a @flag(long).)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm leaning towards this answer to always export long but it would be great to hear other opinions. I almost always want a long-named flag when I have a short and it's difficult to come up with reasons for why that would be a problem. On the other hand, the way we would have to back out of this implementation once we standardize it is awkward in case we really do find a use-case where providing a long-named flag is not desired. A contrived example would be for a drop-in replacement of an existing CLI that only wants to accept inputs they have tests for (aka short-names), then we would need to support something like @flag(short, no-long).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's a good case to think about. My expectation here is that, for a large number of reasons, this automatic CLI Embedding won't be able to emulate the variety of CLI syntaxes in use for tools today, so we shouldn't worry too much about these exact-emulation scenarios. On the flip side, thinking about this issue afresh, it seems like the "adding CLI annotations should always be backwards-compatible" principle mentioned below is a useful one to preserve.


```rust
@flag(short)
help: bool

verbose: bool

@flag(short = "x", long)
execute: bool
```

### Usage
```bash
$ foo -h
$ foo --verbose
$ foo -x
$ foo --execute -x --verbose
```

## Short Named Arguments
* By default named arguments are "long" (inferred as `@arg(pos, named, long)`).
* By annotating them with with the `short` parameter, they can be used in short form e.g. `-i`
* Name inference and manual assignment works the same as flags

```rust
import { file } from ...

input: file

@arg(named, short, long)
output: option<file>
```

### Usage
```bash
$ foo ./input.txt --output ./output.txt
$ foo ./input.txt -o ./output.txt
```