Move design documents

This commit is contained in:
Carl Lerche 2014-03-10 14:47:10 -07:00
parent 3316e85ab5
commit 8bc5b96a55
2 changed files with 294 additions and 0 deletions

152
DESIGN/DESIGN.md Normal file
View File

@ -0,0 +1,152 @@
## Subcommands
The top-level `cargo` command delegates to sub-commands named
`cargo-foo`.
```
$ cargo compile
# delegates to cargo-compile
```
By default, Cargo will come with a set of built-in commands that don't
need to be on the `$PATH`, but additional commands can be added to the
`$PATH`.
There will also be an additional configuration for locating Cargo
subcommands that are not on the `$PATH`.
### Input/Output
By default, Cargo subcommands are built by implementing the
`CargoCommand` trait. This trait will pass structured data to the
command.
By default, commands will communicate with each other via JSON data, and
the `CargoCommand` trait will convert the JSON data into the structured
data needed by the command. All commands must implement JSON data
output.
Commands must also implement human-readable output, and may implement
additional output forms (such as tab- or space-separated output) for use
in other scripting languages.
```rs
// The main entry point for new commands to implement
trait CargoCommand<T, U> {
fn execute<L: CargoLogger>(input: T, logger: L) -> Result<U, CargoErr>;
}
// For now, the raw IPC communication is represented as JSON primitive
// values. The ConvertToRaw trait below converts a string protocol into the
// Raw format. Obviously, the JSON string protocol can trivially be
// converted, but other line protocols need to be defined on a
// case-by-case basis.
type Raw = serialize::json::Json;
type Flags = Map<~str, serialize::json::Json>
// This is a list of available IPC String protocols. To start, we'll
// support JSON and an (optional) arbitrary type-defined line protocol.
enum Input {
JSONString(~str),
LineOrientedString(~str)
}
// This trait supports converting any supported input form into Raw.
trait ConvertToRaw<Input> {
fn convert(input: Input) -> Raw;
}
// This is the runner implementation. It will not need to be implemented
// by individual commands.
fn execute_command<Config, Output, C: CargoCommand, L: Logger>(command: C, config: Config, logger: L) -> Output {
match command.execute(input, logger) {
Ok(success) => {
// serialize success
},
Err(failure) => {
// error handling/output
}
}
}
// This is an example configuration. It is the combination of the Raw
// input from a previous command and any flags passed to this command.
// Top-level commands will mostly be configured via flags -- plumbing
// commands will be mostly configured via Raw.
//
// Note that because configurations serve as both input and output, and
// the ConvertToRaw trait handles both sides of the pipe, these definitions
// are not part of an individual command. Some configuration structures
// may even be used by multiple different commands.
struct CompileConfig {
flags: ~[~str],
path: ~[~str],
lib_path: ~str
}
struct CompileConfigBuilder {
flags: Option<~[~str]>,
path: Option<~[~str]>,
lib_path: Option<~str>
}
// For now, configurations manually convert the Flags and Raw into a
// single configuration object. This is the main point where a failure
// can occur that is not type-checked. All other functions receive the
// structured type and will get compiler help.
impl CompileConfig {
pub fn deserialize(flags: Flags, raw: Raw) -> CompileConfig {
CompileConfig{ flags: raw.at("flags"), path: raw.at("path"), lib_path: flags.at("lib_path") }
}
}
// Configurations must implement ConvertIntoRaw<JSONString> and may
// implement other ConvertIntoRaw converters.
impl ConvertToRaw<JSONString> for CompileConfig {
fn convert(input: JSONString) -> Raw {
}
}
impl ConvertToRaw<LineOrientedString> for CompileConfig {
fn convert(input: LineOrientedString) -> Raw {
}
}
impl ConvertFlags for CompileConfig {
fn convert(input: FlagDefinition) -> Flags {
}
}
// Commands are simple objects that implement CargoCommand for a given
struct CompileCommand;
impl CompileCommand {
fn new() -> CompileCommand { CompileCommand }
}
impl CargoCommand<CompileConfig, CompileOutput> for CompileCommand {
fn execute<L: CargoLogger>(input: CompileConfig, logger: L) -> Result<CompileOutput, CargoErr>;
}
}
fn main() {
let args = parse_arguments(f);
let config = process_args_and_stdin(args); // { "flags": [ ... ] }
let command = CompileCommand::new()
let logger = CargoLogger::for(config);
let result = execute_command(command, config, logger);
// deal with serialized output or error
}
fn process_args_and_stdin(args: Flags) -> CompileConfig {
// delegate to other generic function; Flags tells us which serializer
// to use
}
```
## Configuration

142
DESIGN/MANIFEST.md Normal file
View File

@ -0,0 +1,142 @@
The manifest file (`Cargo.toml`) is still under active development. This document captures the current thinking on the design.
## Sections
The `Cargo.toml` file contains several top-level sections:
* `[project]`: project-specific details, such as name, version and author
* `[[lib]]`: information about the main library file, if one exists. By
default, the main library file is `src/<package-name>.rs`
* `[[executable]]`: optionally repeated information about executables
that the project is generating. This can both be used for projects
that primarily build executables, as well as projects that contain
utility executables (such as an HTTP library that comes with a web
server executable).
## Extensibility
In general, any unknown attributes are ignored for future extensibility.
Future plugins may also use this capability.
The project uses the `Decoder` in `rust-toml` to decode the manifest
into Rust structs that are used throughout the built-in commands.
## The `[project]` Section
* `name`: the name of the project (`~str`)
* `version`: the version of the project, (`~str` that can be parsed
via `semver::parse`)
* `readme`: a Markdown-formatted file in the project that can be used as
a description of the document in indexes (`Option<Path>`, relative to
the project root, defaults to "./README.md", if found).
* `tags`: an array of tags that can be used in indexes (`~[~str]`)
* `authors`: a list of authors in `name <email>` format (`~[~str]`). At
least one `author` with email will probably be required to submit to
the Cargo repository.
* `src`: the root directory containing source files (`Option<Path>`,
relative to the project root, defaults to `src`)
## The `[[lib]]` Section
At the moment, Cargo only supports a single lib per package. We use the
array format for future extensibility.
We only plan to support a single lib at the moment because if you have
multiple libs, you would want projects to be able to depend on them
separately. If you don't care about that, why do you have separate libs?
* `name`: the name of the library (`~str`, `hammer` would create a `libhammer`)
* `path`: the location of the main crate file (`Option<Path>`, defaults to
`src/<name>.rs`)
Note that we plan to support multiple `Cargo.toml` files in Cargo's git
support, so you don't have to have a separate git repository per
library.
## The `[[executable]]` Section
The `executable` section is optionally repeated. It is designed for
projects whose main raison d'être is a single executable, or for projects
that want to provide utility executables alongside a primary library.
If an executable has a different set of flags or dependencies from the
main library, it should be shipped as a separate package with a
dependency on the main library to keep the usage requirements of the
standalone library limited to the bare minimum requirements.
* `name`: the name of the executable (`~str`, `hammer` would create a
`hammer` executable)
* `path`: the location of the main crate file for the executable
(`Option<Path>`, defaults to `src/<name>.rs` if the project has only
an executable, `src/bin/<name>.rs` if the project has both a lib and
executable, see below)
## Projects Containing Both `lib` and `executable`
Most projects will primarily produce either a library or an executable.
Such projects should name the main crate file `<projectname>.rs` and
omit the optional `path` from the `lib` or `executable` sections.
Projects that contain both a library and one or more executables should
generally use `<projectname>.rs` for their library, and `bin/*.rs`
for the executables.
These rules are also the default behavior if `path` is left off of `lib`
or `executable` sections.
## Example Manifests
Simple `lib`:
```toml
[project]
name = "hammer"
version = "0.1.0"
readme = "README.md"
authors = ["Yehuda Katz <wycats@gmail.com>"]
[[lib]]
name = "hammer"
```
Simple `executable`:
```toml
[project]
name = "skylight"
version = "0.5.0"
authors = ["Tom Dale <tom@tomdale.net>", "Carl Lerche <me@carllerche.com>"]
tags = ["performance", "profiling"]
[[executable]]
name = "skylight"
path = "bin/skylight.rs" # supports existing project structures
```
A project with both a lib and an `executable`:
```toml
[project]
name = "skylight"
version = "0.5.0"
authors = ["Tom Dale <tom@tomdale.net>", "Carl Lerche <me@carllerche.com>"]
tags = ["performance", "profiling"]
[[lib]]
name = "skylight" # path defaults to src/skylight.rs
[[executable]]
name = "skylight" # path defaults to src/bin/skylight.rs
[[executable]]
name = "skylight-setup" # path defaults to src/bin/skylight-setup.rs
```