From 45599b887b7a49801da20ac9c1f63a460c5cec4c Mon Sep 17 00:00:00 2001 From: Zapolsky Anton Date: Sat, 20 Oct 2018 14:28:11 +0300 Subject: [PATCH] Rewrite bin/init_exercise.py in Rust (#660) * init_exercise: Generated the crate with 'cargo new' * init_exercise: Added command-line arguments via clap * init_exercise: Added configure command * init_exercise: Replaced the dont-update-config flag with the no-configure flag * init_exercise: Added init_app and process_matches functions * init_exercise: Added cmd module * init_exercise: Added process_matches to the generate module * init_exercise: Added exercise template generation * init_exercise: Added test suite and example.rs generation * init_exercise: Added get_canonical_data function * init_exercise: Added generate_standard_exercise_template function * init_exercise: Renamed generate_standard_exercise_template to generate_default_meta * init_exercise: Added update_cargo_toml function * init_exercise: Added prepend text to the test suite * init_exercise: Added generation of the property functions to the test suite * init_exercise: Applied suggestions from clippy * init_exercise: Added test suite functions generation * init_exercise: Added generate_readme function * init_exercise: Configured std::process::Command calls to wait until the process is finished running * init_exercise: Renamed 'init_exercise' crate to 'exercise' crate * exercise: Added shell script for building exercise crate * exercise: Updated build_exercise_crate.sh script to look for Windows builds * exercise: Removed redundant echos from build_exercise_crate.sh script * exercise: Removed run_configure argument from the generate_exercise function * exercise: Added configure_exercise call to the generate subcommand * exercise: Added utils module * exercise: Implemented the user configuration reading for the configure subcommand * exercise: Implemented the writing of the updated configuration to the config.json * exercise: Added run_configlate_command to the utils module * exercise: Replaced the configlet call code with the run_configlet_command function call * exercise: Added configlet fmt call to the configure_exercise funtion * exercise: Replace 'null' with 'hello-world' as default unlocked_by field value * exercise: Added configure_exercise call for the configure subcommand * exercise: Fixed typo: 'run_configlate_command' -> 'run_configlet_command' * exercise: configure subcommand now updates existing exercise config * exercise: configure subcommand now allows to move the existing exercise config depending on the chosen difficulty * exercise: Replaced the 'None' with 'hello-world' in the unlocked_by field input message * exercise: Moved the processing of ArgMatches to the main module for the generate subcommand * exercise: Replaced &Vec argument type with &[Value] * exercise: Replaced the 'no-configure' flag with the 'configure' flag for the generate and update subcommands * exercise: Added update_exercise call to the main module * exercise: Added check if exercise exists for the update subcommand * exercise: Moved get_canonical_data function to the utils module * exercise: Added get_tests_content function to the utils module * exercise: Added formatting functions for the 'property' and 'description' fields of the exercise * exercise: Implemented apply_diffs function for the update subcommand * exercise: Replaced apply_diffs function get_diffs function * exercise: Implemented the update of the tests file * exercise: Added use_maplit flag for the update subcommand * README: Replaced the 'init_exercise.py' script description with the 'exercise' crate description * README: Fixed typo - realease -> release * build_exercise_crate: Fixed the Windows release path check * exercise: Removed the authors field from the Cargo.toml and the '--help' output * exercise: Added space to the user difficulty input prompt * exercise: Added the newline at the end of the default generated '.meta/description.md' * exercise: Moved exercise_exists function to the utils module * exercise: Replaced the panic in the exercise exists checks with println and return * exercise: Made the [utility] link to point at the exercise crate url on the Github in the test suite template * exercise: Removed the blank line in the generated test case if no comments are present * exercise: 'update' subcommand now updates the version of the exercise in the Cargo.toml file * exercise: Moved the update_cargo_toml_version function to the utils module * exercise: Made 'configure' subcommand to retain the existing values as default values, if an exercise exists in config.json * exercise: Made 'configure' subcommand accept difficulties equal to or greater than the exercise which unlocks the configured exercise * exercise: Added an option to restart the positioning binary search * exercise: Moved the crate into the 'util' directory * travis: Added a script to check if the 'exercise' crate compiles if it was modified * check-exercise-crate: Replaced the 'cargo build --release' command with the 'cargo check' command * exercise: Replaced the locating of the 'rustfmt' utility via 'which' command with PATH variable parsing * exercise: Simplified the exercise_exists function * exercise: Fixed typo in the 'exercise does not exist' message * exercise: Specified the exercise difficulty prompt * exercise: Specified that the new exercise is generated via 'exercise' utility * exercise: Fixed comment typo * exercise: Inlined the urls in the generated tests suite template * exercise: Replaced the get_track_root function with lazy_static TRACK_ROOT value * README: Updated the 'exercise' crate section to mention the openssl dependency * exercise: Updated the crate's version to '1.0.0' * init_exercise.py: Removed the script from the track * Revert "init_exercise.py: Removed the script from the track" This reverts commit d8d464b7980603dbdcddd42af3cefaea1c7d9749. That commit caused a merge conflict. * fix readme possessive typo --- .gitignore | 4 +- .travis.yml | 1 + README.md | 2 +- _test/check-exercise-crate.sh | 34 + bin/build_exercise_crate.sh | 26 + util/exercise/Cargo.lock | 1241 ++++++++++++++++++++++++++++ util/exercise/Cargo.toml | 12 + util/exercise/src/cmd/configure.rs | 310 +++++++ util/exercise/src/cmd/generate.rs | 283 +++++++ util/exercise/src/cmd/mod.rs | 3 + util/exercise/src/cmd/update.rs | 142 ++++ util/exercise/src/main.rs | 96 +++ util/exercise/src/utils.rs | 291 +++++++ 13 files changed, 2443 insertions(+), 2 deletions(-) create mode 100755 _test/check-exercise-crate.sh create mode 100755 bin/build_exercise_crate.sh create mode 100644 util/exercise/Cargo.lock create mode 100644 util/exercise/Cargo.toml create mode 100644 util/exercise/src/cmd/configure.rs create mode 100644 util/exercise/src/cmd/generate.rs create mode 100644 util/exercise/src/cmd/mod.rs create mode 100644 util/exercise/src/cmd/update.rs create mode 100644 util/exercise/src/main.rs create mode 100644 util/exercise/src/utils.rs diff --git a/.gitignore b/.gitignore index adebe4f36..67cf67b83 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,6 @@ tmp bin/configlet bin/configlet.exe -exercises/*/Cargo.lock \ No newline at end of file +bin/exercise +bin/exercise.exe +exercises/*/Cargo.lock diff --git a/.travis.yml b/.travis.yml index 0a0e6a008..fb99e10ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ script: - "./bin/configlet lint ." - "./_test/verify-exercise-difficulties.sh" - "./_test/check-exercises-for-authors.sh" +- "sh ./_test/check-exercise-crate.sh" sudo: false rust: - stable diff --git a/README.md b/README.md index 64465d1e6..290f8bf3f 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Please see the documentation about [adding new exercises](https://github.com/exe Note that: -- The simplest way to generate a project template is to run `bin/init_exercise.py`. You'll need a Python installation >= 3.5 in order to run this script, but it will automate most of the following points for you. +- The simplest way to generate, update or configure an exercise is to use the [exercise](https://github.com/exercism/rust/tree/master/util/exercise) utility provided in this repository. To compile the utility you can use the [bin/build_exercise_crate.sh](https://github.com/exercism/rust/tree/master/bin/build_exercise_crate.sh) script or, if the script does not work for you, use the `cargo build --release` command in the `util/exercise/` directory and then copy the `exercise` binary from the `util/exercise/target/release/` directory into the `bin/` directory. Use `bin/exercise --help` to learn about the existing commands and their possible usage. Please note that this utility depends on the `reqwest` crate and therefore you may need to install its [required libraries](https://github.com/seanmonstar/reqwest#requirements) (namely `openssl`) in your system. - Each exercise must stand on its own. Do not reference files outside the exercise directory. They will not be included when the user fetches the exercise. diff --git a/_test/check-exercise-crate.sh b/_test/check-exercise-crate.sh new file mode 100755 index 000000000..27935079f --- /dev/null +++ b/_test/check-exercise-crate.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env sh + +# A script to ensure that the util/exercise crate builds after it was modified. + +EXERCISE_CRATE_PATH="util/exercise" + +CURRENT_BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) + +if [ "$CURRENT_BRANCH_NAME" != "master" ]; then + # Check the changes on the current branch against master branch + git diff --name-only master | grep "$EXERCISE_CRATE_PATH" +else + # Check the commits on the master branch made during the week + # This is because Travis cron is set to test the master branch weekly. + git diff --name-only "@{7 days ago}" | grep "$EXERCISE_CRATE_PATH" +fi + +if [ $? != 0 ]; then + echo "exercise crate was not modified. The script is aborted." + + exit 0 +fi + +TRACK_ROOT="$(git rev-parse --show-toplevel)" + +if !(cd "$TRACK_ROOT/$EXERCISE_CRATE_PATH" && cargo check); then + echo "\nAn error has occurred while building the exercise crate.\nPlease make it compile." + + exit 1 +else + echo "\nexercise crate has been successfully built." + + exit 0 +fi diff --git a/bin/build_exercise_crate.sh b/bin/build_exercise_crate.sh new file mode 100755 index 000000000..f77f4bd8a --- /dev/null +++ b/bin/build_exercise_crate.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# Compile the 'exercise' crate and put it in the 'bin/' folder + +TRACK_ROOT="$(git rev-parse --show-toplevel)" + +EXERCISE_CRATE_PATH="$TRACK_ROOT/util/exercise" + +BIN_DIR_PATH="$TRACK_ROOT/bin" + +( + cd "$EXERCISE_CRATE_PATH" + + echo "Building exercise crate" + + cargo build --release + + RELEASE_PATH="$EXERCISE_CRATE_PATH/target/release/exercise" + + if [ -f "$RELEASE_PATH.exe" ]; then + RELEASE_PATH="$RELEASE_PATH.exe" + fi + + echo "Copying exercise crate from $RELEASE_PATH into $BIN_DIR_PATH" + + cp "$RELEASE_PATH" "$BIN_DIR_PATH" +) diff --git a/util/exercise/Cargo.lock b/util/exercise/Cargo.lock new file mode 100644 index 000000000..7a6253aae --- /dev/null +++ b/util/exercise/Cargo.lock @@ -0,0 +1,1241 @@ +[[package]] +name = "adler32" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "arrayvec" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "atty" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "base64" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "build_const" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bytes" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cc" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "clap" +version = "2.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "core-foundation-sys" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crc" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-deque" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "dtoa" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "encoding_rs" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "exercise" +version = "1.0.0" +dependencies = [ + "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-cpupool" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "httparse" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hyper" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hyper-tls" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "idna" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "iovec" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itoa" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "language-tags" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazycell" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.43" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libflate" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lock_api" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memoffset" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "mime" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mime_guess" +version = "2.0.0-alpha.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "mime 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "phf 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_codegen 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio-uds" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "native-tls" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "net2" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "nodrop" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num_cpus" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl" +version = "0.9.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.35 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl-sys" +version = "0.9.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "owning_ref" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "phf" +version = "0.7.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_codegen" +version = "0.7.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_generator 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_generator" +version = "0.7.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_shared" +version = "0.7.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pkg-config" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_syscall" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "relay" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "reqwest" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libflate 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.76 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_urlencoded 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ryu" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "safemem" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "schannel" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scoped-tls" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "scopeguard" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "security-framework" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "security-framework-sys" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_json" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.76 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_urlencoded" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.76 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "siphasher" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "slab" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "smallvec" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "stable_deref_trait" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "strsim" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "tempdir" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "termion" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "textwrap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-current-thread 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-fs 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-udp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-uds 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-codec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-core" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", + "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-current-thread" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-executor" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-fs" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-io" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-reactor" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-service" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-tcp" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-threadpool" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-timer" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-tls" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-udp" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-uds" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-uds 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "toml" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.76 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "try-lock" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicase" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicase" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-width" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "url" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "uuid" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "uuid" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "vcpkg" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "version_check" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "want" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" +"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" +"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" +"checksum base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "85415d2594767338a74a30c1d370b2f3262ec1b4ed2d7bba5b3faf4de40467d9" +"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" +"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" +"checksum byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90492c5858dd7d2e78691cfb89f90d273a2800fc11d98f60786e5d87e2f83781" +"checksum bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e178b8e0e239e844b083d5a0d4a156b2654e67f9f80144d48398fcd736a24fb8" +"checksum cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)" = "c37f0efaa4b9b001fa6f02d4b644dee4af97d3414df07c51e3e4f015f3a3e131" +"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" +"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" +"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +"checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67" +"checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d" +"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" +"checksum crossbeam-deque 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3486aefc4c0487b9cb52372c97df0a48b8c249514af1ee99703bf70d2f2ceda1" +"checksum crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30fecfcac6abfef8771151f8be4abc9e4edc112c2bcb233314cafde2680536e9" +"checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015" +"checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" +"checksum encoding_rs 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2a91912d6f37c6a8fef8a2316a862542d036f13c923ad518b5aca7bcaac7544c" +"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "884dbe32a6ae4cd7da5c6db9b78114449df9953b8d490c9d7e1b51720b922c62" +"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" +"checksum httparse 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7b6288d7db100340ca12873fd4d08ad1b8f206a9457798dfb17c018a33fee540" +"checksum hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)" = "34a590ca09d341e94cddf8e5af0bbccde205d5fbc2fa3c09dd67c7f85cea59d7" +"checksum hyper-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ffb1bd5e518d3065840ab315dbbf44e4420e5f7d80e2cb93fa6ffffc50522378" +"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" +"checksum itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5adb58558dcd1d786b5f0bd15f3226ee23486e24b7b58304b60f64dc68e62606" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" +"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" +"checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7" +"checksum lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef" +"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" +"checksum libflate 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7d4b4c7aff5bac19b956f693d0ea0eade8066deb092186ae954fa6ba14daab98" +"checksum lock_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "949826a5ccf18c1b3a7c3d57692778d21768b79e46eb9dd07bfc4c2160036c54" +"checksum log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cba860f648db8e6f269df990180c2217f333472b4a6e901e97446858487971e2" +"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" +"checksum mime 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "4b082692d3f6cf41b453af73839ce3dfc212c4411cbb2441dff80a716e38bd79" +"checksum mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "30de2e4613efcba1ec63d8133f344076952090c122992a903359be5a4f99c3ed" +"checksum mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)" = "4fcfcb32d63961fb6f367bfd5d21e4600b92cd310f71f9dca25acae196eb1560" +"checksum mio-uds 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "84c7b5caa3a118a6e34dbac36504503b1e8dc5835e833306b9d6af0e05929f79" +"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f74dbadc8b43df7864539cedb7bc91345e532fdd913cfdc23ad94f4d2d40fbc0" +"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" +"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" +"checksum openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "a3605c298474a3aa69de92d21139fb5e2a81688d308262359d85cdd0d12a7985" +"checksum openssl-sys 0.9.35 (registry+https://github.com/rust-lang/crates.io-index)" = "912f301a749394e1025d9dcddef6106ddee9252620e6d0a0e5f8d0681de9b129" +"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" +"checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" +"checksum parking_lot_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "06a2b6aae052309c2fd2161ef58f5067bc17bb758377a0de9d4b279d603fdd8a" +"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum phf 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "cec29da322b242f4c3098852c77a0ca261c9c01b806cae85a5572a1eb94db9a6" +"checksum phf_codegen 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "7d187f00cd98d5afbcd8898f6cf181743a449162aeb329dcd2f3849009e605ad" +"checksum phf_generator 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "03dc191feb9b08b0dc1330d6549b795b9d81aec19efe6b4a45aec8d4caee0c4b" +"checksum phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "b539898d22d4273ded07f64a05737649dc69095d92cb87c7097ec68e3f150b93" +"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" +"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" +"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" +"checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2" +"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" +"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" +"checksum reqwest 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)" = "738769ec83daf6c1929dc9dae7d69ed3779b55ae5c356e989dcd3aa677d8486e" +"checksum ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7153dd96dade874ab973e098cb62fcdbb89a03682e46b144fd09550998d4a4a7" +"checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" +"checksum schannel 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "dc1fabf2a7b6483a141426e1afd09ad543520a77ac49bd03c286e7696ccfd77f" +"checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" +"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"checksum security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "dfa44ee9c54ce5eecc9de7d5acbad112ee58755239381f687e564004ba4a2332" +"checksum security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "5421621e836278a0b139268f36eee0dc7e389b784dc3f79d8f11aabadf41bead" +"checksum serde 1.0.76 (registry+https://github.com/rust-lang/crates.io-index)" = "d00c69ae39089576cddfd235556e3b21bf41c2d80018063cb5ab8a1183c917fd" +"checksum serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "44dd2cfde475037451fa99b7e5df77aa3cfd1536575fa8e7a538ab36dcde49ae" +"checksum serde_urlencoded 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aaed41d9fb1e2f587201b863356590c90c1157495d811430a0c0325fe8169650" +"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" +"checksum slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d" +"checksum smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "153ffa32fd170e9944f7e0838edf824a754ec4c1fc64746fcc9fe1f8fa602e5d" +"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" +"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" +"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" +"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" +"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" +"checksum tokio 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fbb6a6e9db2702097bfdfddcb09841211ad423b86c75b5ddaca1d62842ac492c" +"checksum tokio-codec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "881e9645b81c2ce95fcb799ded2c29ffb9f25ef5bef909089a420e5961dd8ccb" +"checksum tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71" +"checksum tokio-current-thread 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fdfb899688ac16f618076bd09215edbfda0fd5dfecb375b6942636cb31fa8a7" +"checksum tokio-executor 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "84823b932d566bc3c6aa644df4ca36cb38593c50b7db06011fd4e12e31e4047e" +"checksum tokio-fs 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b5cbe4ca6e71cb0b62a66e4e6f53a8c06a6eefe46cc5f665ad6f274c9906f135" +"checksum tokio-io 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8d6cc2de7725863c86ac71b0b9068476fec50834f055a243558ef1655bbd34cb" +"checksum tokio-reactor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4bfbaf9f260635649ec26b6fb4aded03887295ffcd999f6e43fd2c4758f758ea" +"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" +"checksum tokio-tcp 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5b4c329b47f071eb8a746040465fa751bd95e4716e98daef6a9b4e434c17d565" +"checksum tokio-threadpool 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a5758cecb6e0633cea5d563ac07c975e04961690b946b04fd84e7d6445a8f6af" +"checksum tokio-timer 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d03fa701f9578a01b7014f106b47f0a363b4727a7f3f75d666e312ab7acbbf1c" +"checksum tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "772f4b04e560117fe3b0a53e490c16ddc8ba6ec437015d91fa385564996ed913" +"checksum tokio-udp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "da941144b816d0dcda4db3a1ba87596e4df5e860a72b70783fe435891f80601c" +"checksum tokio-uds 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "424c1ed15a0132251813ccea50640b224c809d6ceafb88154c1a8775873a0e89" +"checksum toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a0263c6c02c4db6c8f7681f9fd35e90de799ebd4cfdeab77a38f4ff6b3d8c0d9" +"checksum try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee2aa4715743892880f70885373966c83d73ef1b0838a664ef0c76fffd35e7c2" +"checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" +"checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a" +"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" +"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" +"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6" +"checksum uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e1436e58182935dcd9ce0add9ea0b558e8a87befe01c1a301e6020aeb0876363" +"checksum uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dab5c5526c5caa3d106653401a267fed923e7046f35895ffcb5ca42db64942e6" +"checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d" +"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +"checksum version_check 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7716c242968ee87e5542f8021178248f267f295a5c4803beae8b8b7fd9bc6051" +"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +"checksum want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a05d9d966753fa4b5c8db73fcab5eed4549cfe0e1e4e66911e5564a0085c35d1" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" diff --git a/util/exercise/Cargo.toml b/util/exercise/Cargo.toml new file mode 100644 index 000000000..51bb8264e --- /dev/null +++ b/util/exercise/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "exercise" +version = "1.0.0" +description = "An utility for creating or updating the exercises on the Exercism Rust track" + +[dependencies] +clap = "2.32.0" +reqwest = "0.8.8" +serde_json = "1.0.26" +toml = "0.4" +lazy_static = "1.1.0" +uuid = { version = "0.7", features = ["v4"] } diff --git a/util/exercise/src/cmd/configure.rs b/util/exercise/src/cmd/configure.rs new file mode 100644 index 000000000..a4478db1b --- /dev/null +++ b/util/exercise/src/cmd/configure.rs @@ -0,0 +1,310 @@ +use serde_json::{self, Value}; +use std::{ + fs, + io::{stdin, stdout, Write}, + path::Path, +}; +use utils; +use uuid::Uuid; + +fn get_user_input(prompt: &str) -> String { + print!("{}", prompt); + + let mut buffer = String::new(); + + stdout().flush().unwrap(); + + stdin() + .read_line(&mut buffer) + .expect("Failed to get user input."); + + buffer.trim().to_string() +} + +fn get_user_config(exercise_name: &str, config_content: &Value) -> Value { + let existing_config: Option<&Value> = config_content["exercises"] + .as_array() + .unwrap() + .iter() + .find(|exercise| exercise["slug"] == exercise_name); + + let uuid = if let Some(existing_config) = existing_config { + existing_config["uuid"].as_str().unwrap().to_string() + } else { + Uuid::new_v4().to_hyphenated().to_string() + }; + + let core = false; + + let unlocked_by = loop { + let default_value = if let Some(existing_config) = existing_config { + existing_config["unlocked_by"].as_str().unwrap() + } else { + "hello-world" + }; + + let user_input = get_user_input(&format!( + "Exercise slug which unlocks this (blank for '{}'): ", + default_value + )); + + if user_input.is_empty() { + break default_value.to_string(); + } else if !config_content["exercises"] + .as_array() + .unwrap() + .iter() + .any(|exercise| exercise["slug"] == user_input) + { + println!("{} is not an existing exercise slug", user_input); + + continue; + } else { + break user_input; + }; + }; + + let difficulty = loop { + let unlocked_by_difficulty = config_content["exercises"] + .as_array() + .unwrap() + .iter() + .find(|exercise| exercise["slug"] == unlocked_by) + .unwrap()["difficulty"] + .as_u64() + .unwrap(); + + let available_difficulties: Vec = [1, 4, 7, 10] + .iter() + .skip_while(|&&difficulty| difficulty < unlocked_by_difficulty) + .cloned() + .collect(); + + let default_value = if let Some(existing_config) = existing_config { + existing_config["difficulty"].as_u64().unwrap() + } else { + *available_difficulties.first().unwrap() + }; + + let user_input = get_user_input(&format!( + "Difficulty for this exercise {:?} (blank for {}): ", + available_difficulties, default_value + )); + + if user_input.is_empty() { + break default_value; + } else if let Ok(difficulty) = user_input.parse::() { + if !available_difficulties.contains(&difficulty) { + println!( + "Difficulty should be {:?}, not '{}'.", + available_difficulties, difficulty + ); + + continue; + } + + break difficulty; + } else { + println!("Difficulty should be a number, not '{}'.", user_input); + + continue; + } + }; + + let topics = loop { + let default_value = if let Some(existing_config) = existing_config { + let topics = &existing_config["topics"]; + + if topics.is_array() { + topics + .as_array() + .unwrap() + .iter() + .map(|topic| topic.as_str().unwrap().to_string()) + .collect::>() + } else { + vec![topics.as_str().unwrap().to_string()] + } + } else { + vec![exercise_name.to_string()] + }; + + let user_input = get_user_input(&format!( + "List of topics for this exercise, comma-separated (blank for {:?}): ", + default_value, + )); + + if user_input.is_empty() { + break default_value; + } + + let topics = user_input + .split(',') + .map(|topic| topic.trim().to_string()) + .filter(|topic| !topic.is_empty()) + .collect::>(); + + if topics.is_empty() { + println!("Must enter at least one topic"); + + continue; + } + + break topics; + }; + + json!({ + "slug": exercise_name, + "uuid": uuid, + "core": core, + "unlocked_by": unlocked_by, + "difficulty": difficulty, + "topics": topics + }) +} + +fn choose_exercise_insert_index( + exercise_name: &str, + exercises: &[Value], + difficulty: &Value, +) -> usize { + loop { + let exercises_with_similar_difficulty = exercises + .iter() + .enumerate() + .filter(|(_, exercise)| exercise["difficulty"] == *difficulty) + .map(|(index, exercise)| (index, exercise["slug"].as_str().unwrap())) + .collect::>(); + + let mut start_index = 0; + + let mut end_index = exercises_with_similar_difficulty.len() - 1; + + let insert_index = loop { + if start_index == end_index { + break start_index; + } + + let middle_index = start_index + ((end_index - start_index) / 2); + + let user_input = get_user_input(&format!( + "Is {} easier then {}? (y/N): ", + exercise_name, exercises_with_similar_difficulty[middle_index].1 + )); + + if user_input.to_lowercase().starts_with('y') { + end_index = middle_index; + } else { + start_index = middle_index + 1; + } + }; + + let insert_index = exercises_with_similar_difficulty[insert_index].0; + + let prompt = if insert_index == 0 { + format!( + "{} is the easiest exercise of difficulty {}.", + exercise_name, *difficulty + ) + } else if insert_index == exercises.len() - 1 { + format!( + "{} is the hardest exercise of difficulty {}.", + exercise_name, *difficulty + ) + } else { + format!( + "{} is placed between {} and {} exercises in difficulty.", + exercise_name, + exercises[insert_index - 1]["slug"].as_str().unwrap(), + exercises[insert_index]["slug"].as_str().unwrap(), + ) + }; + + let user_input = get_user_input(&format!( + "You have configured that {}.\nIs this correct? (y/N): ", + prompt + )); + + if user_input.to_lowercase().starts_with('y') { + break insert_index; + } + } +} + +fn insert_user_config(exercise_name: &str, config_content: &mut Value, user_config: Value) { + let exercises = config_content["exercises"].as_array_mut().unwrap(); + + let insert_index = + choose_exercise_insert_index(exercise_name, exercises, &user_config["difficulty"]); + + exercises.insert(insert_index, user_config); +} + +fn update_existing_config(exercise_name: &str, config_content: &mut Value, user_config: Value) { + let exercises = config_content["exercises"].as_array_mut().unwrap(); + + let existing_exercise_index = exercises + .iter() + .position(|exercise| exercise["slug"] == exercise_name) + .unwrap(); + + let insert_index = + if exercises[existing_exercise_index]["difficulty"] == user_config["difficulty"] { + existing_exercise_index + } else { + choose_exercise_insert_index(exercise_name, &exercises, &user_config["difficulty"]) + }; + + exercises.remove(existing_exercise_index); + + exercises.insert(insert_index, user_config); +} + +pub fn configure_exercise(exercise_name: &str) { + println!( + "Configuring config.json for the {} exercise.", + exercise_name + ); + + let config_path = Path::new(&*utils::TRACK_ROOT).join("config.json"); + + let config_content_string = fs::read_to_string(&config_path) + .expect("Failed to read the contents of the config.json file"); + + let mut config_content: Value = serde_json::from_str(&config_content_string).unwrap(); + + let config_exists = config_content["exercises"] + .as_array() + .unwrap() + .iter() + .any(|exercise| exercise["slug"] == exercise_name); + + let user_config: Value = loop { + let user_config = get_user_config(exercise_name, &config_content); + + let user_input = get_user_input(&format!( + "You have configured the {} exercise as follows:\n{}\nIs this correct? (y/N):", + exercise_name, + serde_json::to_string_pretty(&user_config).unwrap() + )); + + if user_input.to_lowercase().starts_with('y') { + break user_config; + } + }; + + if config_exists { + update_existing_config(exercise_name, &mut config_content, user_config); + } else { + insert_user_config(exercise_name, &mut config_content, user_config); + } + + fs::write( + &config_path, + serde_json::to_string_pretty(&config_content).unwrap(), + ).expect("Failed to write the updated track configuration to the config.json file"); + + println!("Formatting the config.json file via 'bin/configlet fmt'"); + + utils::run_configlet_command("fmt", &["."]); +} diff --git a/util/exercise/src/cmd/generate.rs b/util/exercise/src/cmd/generate.rs new file mode 100644 index 000000000..f8dddf59f --- /dev/null +++ b/util/exercise/src/cmd/generate.rs @@ -0,0 +1,283 @@ +/// This module contains source for the `generate` command. +use serde_json::Value as JsonValue; +use std::{ + collections::HashMap, + fs::{self, File, OpenOptions}, + io::Write, + path::Path, + process::{Command, Stdio}, +}; +use utils; + +static GITIGNORE_CONTENT: &'static str = "# Generated by exercism rust track exercise tool +# will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock +"; + +static EXAMPLE_RS_CONTENT: &'static str = "//! Example implementation +//! +//! - Implement the solution to your exercise here. +//! - Put the stubs for any tested functions in `src/lib.rs`, +//! whose variable names are `_` and +//! whose contents are `unimplemented!()`. +//! - If your example implementation has dependencies, copy +//! `Cargo.toml` into `Cargo-example.toml` and then make +//! any modifications necessary to the latter so your example will run. +//! - Test your example by running `../../bin/test-exercise` +"; + +// Generate .meta directory and it's contents without using the canonical data +fn generate_default_meta(exercise_name: &str, exercise_path: &Path) { + fs::create_dir(exercise_path.join(".meta")).expect("Failed to create the .meta directory"); + + fs::write( + exercise_path.join(".meta").join("description.md"), + "Describe your exercise here.\n\nDon't forget that `README.md` is automatically generated; update this within `.meta/description.md`.\n", + ).expect("Failed to create .meta/description.md file"); + + fs::write( + exercise_path.join(".meta").join("metadata.yml"), + format!( + "---\nblurb: \"{}\"\nsource: \"\"\nsource_url: \"\"", + exercise_name + ), + ).expect("Failed to create .meta/metadata.yml file"); + + let mut tests_file = OpenOptions::new() + .append(true) + .open( + exercise_path + .join("tests") + .join(format!("{}.rs", exercise_name)), + ).unwrap(); + + tests_file.write_all(b"// Add your tests here").unwrap(); +} + +// Generate test suite using the canonical data +fn generate_tests_from_canonical_data( + exercise_name: &str, + exercise_path: &Path, + canonical_data: &JsonValue, + use_maplit: bool, +) { + utils::update_cargo_toml_version(exercise_name, canonical_data); + + let tests_path = exercise_path + .join("tests") + .join(format!("{}.rs", exercise_name)); + + let tests_content = utils::get_tests_content(exercise_name).unwrap_or_else(|_| { + panic!( + "Failed to get the content of the test suite for the '{}' exercise. Aborting.", + exercise_name + ) + }); + + let updated_tests_content = format!( + "//! Tests for {exercise_name} \n\ + //! \n\ + //! Generated by [utility][utility] using [canonical data][canonical_data]\n\ + //! \n\ + //! [utility]: https://github.com/exercism/rust/tree/master/util/exercise\n\ + //! [canonical_data]: https://raw.githubusercontent.com/exercism/problem-specifications/master/exercises/{exercise_name}/canonical-data.json\n\ + \n\ + {} \n\ + ", + tests_content, + exercise_name=exercise_name, + ); + + fs::write(&tests_path, updated_tests_content) + .expect("Failed to update the content of the test suite"); + + let mut property_functions: HashMap<&str, String> = HashMap::new(); + + let mut test_functions: Vec = Vec::new(); + + let cases = canonical_data.get("cases").unwrap(); + + for case in cases.as_array().unwrap().iter() { + if let Some(sub_cases) = case.get("cases") { + for sub_case in sub_cases.as_array().unwrap().iter() { + if let Some(property) = sub_case.get("property") { + let property = property.as_str().unwrap(); + + if !property_functions.contains_key(property) { + property_functions + .insert(property, utils::generate_property_body(property)); + } + } + + test_functions.push(utils::generate_test_function(&sub_case, use_maplit)); + } + } else { + if let Some(property) = case.get("property") { + let property = property.as_str().unwrap(); + + if !property_functions.contains_key(property) { + property_functions.insert(property, utils::generate_property_body(property)); + } + } + + test_functions.push(utils::generate_test_function(&case, use_maplit)); + } + } + + if !test_functions.is_empty() { + let first_test_function = test_functions.remove(0).replace("#[ignore]\n", ""); + + test_functions.insert(0, first_test_function); + } + + let mut tests_file = OpenOptions::new().append(true).open(&tests_path).unwrap(); + + for (property, property_body) in &property_functions { + tests_file + .write_all(property_body.as_bytes()) + .unwrap_or_else(|_| { + panic!( + "Failed to add {} property function to the tests file", + property + ) + }); + } + + tests_file + .write_all(test_functions.join("\n\n").as_bytes()) + .unwrap_or_else(|_| panic!("Failed to add test functions to the test file")); + + utils::rustfmt(&tests_path); +} + +// Run bin/configlet generate command to generate README for the exercise +fn generate_readme(exercise_name: &str) { + println!( + "Generating README for {} via 'bin/configlet generate'", + exercise_name + ); + + let problem_specifications_path = Path::new(&*utils::TRACK_ROOT) + .join("..") + .join("problem-specifications"); + + if !problem_specifications_path.exists() { + let problem_specifications_url = "https://github.com/exercism/problem-specifications.git"; + println!( + "problem-specifications repository not found. Cloning the repository from {}", + problem_specifications_url + ); + + Command::new("git") + .current_dir(&*utils::TRACK_ROOT) + .stdout(Stdio::inherit()) + .arg("clone") + .arg(problem_specifications_url) + .arg(&problem_specifications_path) + .output() + .expect("Failed to clone problem-specifications repo"); + } + + utils::run_configlet_command( + "generate", + &[ + ".", + "--only", + exercise_name, + "--spec-path", + problem_specifications_path.to_str().unwrap(), + ], + ); +} + +// Generate a new exercise with specified name and flags +pub fn generate_exercise(exercise_name: &str, use_maplit: bool) { + if utils::exercise_exists(exercise_name) { + println!( + "Exercise with the name {} already exists. Aborting", + exercise_name + ); + + return; + } + + let exercise_path = Path::new(&*utils::TRACK_ROOT) + .join("exercises") + .join(exercise_name); + + println!( + "Generating a new exercise at the following path: {}", + exercise_path.to_str().unwrap() + ); + + let _cargo_new_output = Command::new("cargo") + .arg("new") + .arg("--lib") + .arg(exercise_path.to_str().unwrap()) + .output() + .expect("Failed to generate a new exercise via 'cargo new' command"); + + fs::write(exercise_path.join(".gitignore"), GITIGNORE_CONTENT) + .expect("Failed to create .gitignore file"); + + if use_maplit { + let mut cargo_toml_file = OpenOptions::new() + .append(true) + .open(exercise_path.join("Cargo.toml")) + .unwrap(); + + cargo_toml_file + .write_all(b"maplit = \"1.0.1\"") + .expect("Failed to add maplit dependency to the Cargo.toml"); + } + + fs::create_dir(exercise_path.join("tests")).expect("Failed to create the tests directory"); + + let mut test_file = File::create( + exercise_path + .join("tests") + .join(format!("{}.rs", exercise_name)), + ).expect("Failed to create test suite file"); + + if use_maplit { + test_file + .write_all(b"#[macro_use]\nextern crate maplit;\n") + .expect("Failed to append maplit crate to the test file."); + } + + test_file + .write_all(&format!("extern crate {};\n", exercise_name.replace("-", "_")).into_bytes()) + .unwrap(); + + test_file + .write_all(&format!("use {}::*;\n\n", exercise_name.replace("-", "_")).into_bytes()) + .unwrap(); + + fs::write(exercise_path.join("example.rs"), EXAMPLE_RS_CONTENT) + .expect("Failed to create example.rs file"); + + if let Some(canonical_data) = utils::get_canonical_data(exercise_name) { + println!("Generating tests from canonical data"); + + generate_tests_from_canonical_data( + &exercise_name, + &exercise_path, + &canonical_data, + use_maplit, + ); + } else { + println!( + "No canonical data for exercise '{}' found. Generating standard exercise template.", + &exercise_name + ); + + generate_default_meta(&exercise_name, &exercise_path); + } + + generate_readme(&exercise_name); +} diff --git a/util/exercise/src/cmd/mod.rs b/util/exercise/src/cmd/mod.rs new file mode 100644 index 000000000..cb0a1f9c3 --- /dev/null +++ b/util/exercise/src/cmd/mod.rs @@ -0,0 +1,3 @@ +pub mod configure; +pub mod generate; +pub mod update; diff --git a/util/exercise/src/cmd/update.rs b/util/exercise/src/cmd/update.rs new file mode 100644 index 000000000..93579604c --- /dev/null +++ b/util/exercise/src/cmd/update.rs @@ -0,0 +1,142 @@ +use serde_json::Value; +use std::{collections::HashSet, fs, path::Path}; +use utils; + +enum DiffType { + NEW, + UPDATED, +} + +fn generate_diff_test(case: &Value, diff_type: &DiffType, use_maplit: bool) -> String { + format!( + "//{}\n{}", + match diff_type { + DiffType::NEW => "NEW", + DiffType::UPDATED => "UPDATED", + }, + utils::generate_test_function(case, use_maplit) + ) +} + +fn generate_diff_property(property: &str) -> String { + format!("//{}\n{}", "NEW", utils::generate_property_body(property)) +} + +fn generate_diffs( + case: &Value, + tests_content: &str, + diffs: &mut HashSet, + use_maplit: bool, +) { + let description = case["description"].as_str().unwrap(); + + let description_formatted = utils::format_exercise_description(description); + + let diff_type = if !tests_content.contains(&format!("test_{}", description_formatted)) { + DiffType::NEW + } else { + DiffType::UPDATED + }; + + if diffs.insert(generate_diff_test(&case, &diff_type, use_maplit)) { + match diff_type { + DiffType::NEW => println!("New test case detected: {}.", description_formatted), + DiffType::UPDATED => println!("Updated test case: {}.", description_formatted), + } + } + + let property = case["property"].as_str().unwrap(); + + let property_formatted = utils::format_exercise_property(property); + + if !tests_content.contains(&format!("process_{}_case", property_formatted)) + && diffs.insert(generate_diff_property(property)) + { + println!("New property detected: {}.", property); + } +} + +fn get_diffs( + exercise_name: &str, + canonical_data: &Value, + tests_content: &str, + use_maplit: bool, +) -> HashSet { + let cases = canonical_data.get("cases").unwrap_or_else(|| { + panic!( + "Failed to get 'cases' field from the canonical data of the '{}' exercise", + exercise_name + ) + }); + + let mut diffs: HashSet = HashSet::new(); + + for case in cases.as_array().unwrap().iter() { + if let Some(sub_cases) = case.get("cases") { + for sub_case in sub_cases.as_array().unwrap().iter() { + generate_diffs(&sub_case, &tests_content, &mut diffs, use_maplit); + } + } else { + generate_diffs(&case, &tests_content, &mut diffs, use_maplit); + } + } + + diffs +} + +fn apply_diffs(exercise_name: &str, diffs: &HashSet, tests_content: &str) { + let updated_tests_content = format!( + "{}\n{}", + tests_content, + diffs + .iter() + .map(|diff| format!("\n{}", diff)) + .collect::() + ); + + let tests_path = Path::new(&*utils::TRACK_ROOT) + .join("exercises") + .join(exercise_name) + .join("tests") + .join(format!("{}.rs", exercise_name)); + + fs::write(&tests_path, updated_tests_content.as_bytes()).unwrap_or_else(|_| { + panic!( + "Failed to update tests file for the '{}' exercise. Aborting.", + exercise_name, + ) + }); + + utils::rustfmt(&tests_path); +} + +pub fn update_exercise(exercise_name: &str, use_maplit: bool) { + if !utils::exercise_exists(exercise_name) { + println!( + "Exercise with the name '{}' does not exist. Aborting", + exercise_name + ); + + return; + } + + let tests_content = utils::get_tests_content(exercise_name).unwrap_or_else(|_| { + panic!( + "Failed to get test content for the '{}' exercise", + exercise_name + ) + }); + + let canonical_data = utils::get_canonical_data(exercise_name).unwrap_or_else(|| { + panic!( + "Failed to get canonical data for the '{}' exercise. Aborting", + exercise_name + ) + }); + + let diffs = get_diffs(exercise_name, &canonical_data, &tests_content, use_maplit); + + apply_diffs(exercise_name, &diffs, &tests_content); + + utils::update_cargo_toml_version(exercise_name, &canonical_data); +} diff --git a/util/exercise/src/main.rs b/util/exercise/src/main.rs new file mode 100644 index 000000000..98d982567 --- /dev/null +++ b/util/exercise/src/main.rs @@ -0,0 +1,96 @@ +extern crate clap; +extern crate reqwest; +#[macro_use] +extern crate serde_json; +#[macro_use] +extern crate lazy_static; +extern crate toml; +extern crate uuid; + +mod cmd; +mod utils; + +use clap::{App, Arg, ArgMatches, SubCommand}; +use cmd::{configure, generate, update}; + +// Creates a new CLI app with appropriate matches +// and returns the initialized matches. +fn init_app<'a>() -> ArgMatches<'a> { + App::new(env!("CARGO_PKG_NAME")) + .version(env!("CARGO_PKG_VERSION")) + .about(env!("CARGO_PKG_DESCRIPTION")) + .subcommand( + SubCommand::with_name("generate") + .about("Generates new exercise") + .arg(Arg::with_name("exercise_name").required(true).help("The name of the generated exercise")) + .arg(Arg::with_name("configure").long("configure").short("c").help( + "If set, the command will edit the config.json file after generating the exercise", + )) + .arg(Arg::with_name("use_maplit").long("use-maplit").short("m").help("Use the maplit crate to improve the readability of the generated test suite")), + ) + .subcommand( + SubCommand::with_name("update") + .about("Updates the specified exercise") + .arg(Arg::with_name("exercise_name").help("The name of the updated exercise")) + .arg(Arg::with_name("use_maplit").long("use-maplit").short("m").help("Use the maplit crate to improve the readability of the updated test suite")) + .arg(Arg::with_name("configure").long("configure").short("c").help( + "If set, the command will edit the config.json file after updating the exercise", + )) + ) + .subcommand( + SubCommand::with_name("configure") + .about("Edits config.json for the specified exercise") + .arg(Arg::with_name("exercise_name").required(true).help("The name of the configured exercise")), + ) + .get_matches() +} + +// Determine which subcommand was used +// and call the appropriate function. +fn process_matches(matches: &ArgMatches) { + match matches.subcommand() { + ("generate", Some(generate_matches)) => { + let exercise_name = generate_matches.value_of("exercise_name").unwrap(); + + let run_configure = generate_matches.is_present("configure"); + + let use_maplit = generate_matches.is_present("use_maplit"); + + generate::generate_exercise(exercise_name, use_maplit); + + if run_configure { + configure::configure_exercise(exercise_name); + } + }, + + ("update", Some(update_matches)) => { + let exercise_name = update_matches.value_of("exercise_name").unwrap(); + + let run_configure = update_matches.is_present("configure"); + + let use_maplit = update_matches.is_present("use_maplit"); + + update::update_exercise(exercise_name, use_maplit); + + if run_configure { + configure::configure_exercise(exercise_name); + } + }, + + ("configure", Some(configure_matches)) => { + configure::configure_exercise(configure_matches.value_of("exercise_name").unwrap()) + } + + ("", None) => { + println!("No subcommand was used.\nUse init_exercise --help to learn about the possible subcommands.") + } + + _ => unreachable!(), + } +} + +fn main() { + let matches = init_app(); + + process_matches(&matches); +} diff --git a/util/exercise/src/utils.rs b/util/exercise/src/utils.rs new file mode 100644 index 000000000..f6b9c7270 --- /dev/null +++ b/util/exercise/src/utils.rs @@ -0,0 +1,291 @@ +use reqwest::{self, StatusCode}; +use serde_json::Value; +use std::{ + env, fs, io, + path::Path, + process::{Command, Stdio}, +}; +use toml::Value as TomlValue; + +lazy_static! { + pub static ref TRACK_ROOT: String = { + let rev_parse_output = Command::new("git") + .arg("rev-parse") + .arg("--show-toplevel") + .output() + .expect("Failed to get the path to the track repo."); + + String::from_utf8(rev_parse_output.stdout) + .unwrap() + .trim() + .to_string() + }; +} + +pub fn run_configlet_command(command: &str, args: &[&str]) { + let track_root = &*TRACK_ROOT; + + let bin_path = Path::new(track_root).join("bin"); + + let configlet_name_unix = "configlet"; + + let configlet_name_windows = "configlet.exe"; + + let configlet_name = if bin_path.join(configlet_name_unix).exists() { + configlet_name_unix + } else if bin_path.join(configlet_name_windows).exists() { + configlet_name_windows + } else { + println!("Configlet not found in the bin directory. Running bin/fetch-configlet."); + + // FIXME: Uses bash script that would not work on Windows. + // RIIR is preferred. + Command::new("bash") + .current_dir(track_root) + .stdout(Stdio::inherit()) + .arg(bin_path.join("fetch-configlet")) + .output() + .expect("Failed to run fetch-configlet script"); + + if bin_path.join(configlet_name_unix).exists() { + configlet_name_unix + } else if bin_path.join(configlet_name_windows).exists() { + configlet_name_windows + } else { + panic!("Could not locate configlet after running bin/fetch-configlet. Aborting"); + } + }; + + Command::new(&bin_path.join(configlet_name)) + .current_dir(track_root) + .stdout(Stdio::inherit()) + .arg(command) + .args(args) + .output() + .expect("Failed to run configlet generate command"); +} + +// Try to get the canonical data for the exercise of the given name +pub fn get_canonical_data(exercise_name: &str) -> Option { + let url = format!("https://raw.githubusercontent.com/exercism/problem-specifications/master/exercises/{}/canonical-data.json", exercise_name); + + let mut response = + reqwest::get(&url).expect("Failed to make HTTP request for the canonical data."); + + if response.status() != StatusCode::Ok { + None + } else { + Some( + response + .json() + .expect("Failed to parse the JSON canonical-data response"), + ) + } +} + +pub fn get_tests_content(exercise_name: &str) -> io::Result { + let tests_path = Path::new(&*TRACK_ROOT) + .join("exercises") + .join(exercise_name) + .join("tests") + .join(format!("{}.rs", exercise_name)); + + fs::read_to_string(tests_path) +} + +pub fn format_exercise_description(description: &str) -> String { + description + .chars() + .filter(|c| c.is_alphanumeric() || *c == ' ') + .collect::() + .replace(" ", "_") + .to_lowercase() +} + +pub fn format_exercise_property(property: &str) -> String { + property.replace(" ", "_").to_lowercase() +} + +pub fn generate_property_body(property: &str) -> String { + format!( + "\ + /// Process a single test case for the property `{property}`\n\ + ///\n\ + /// All cases for the `{property}` property are implemented\n\ + /// in terms of this function.\n\ + /// \n\ + /// Note that you'll need to both name the expected transform which\n\ + /// the student needs to write, and name the types of the inputs and outputs.\n\ + /// While rustc _may_ be able to handle things properly given a working example,\n\ + /// students will face confusing errors if the `I` and `O` types are not concrete.\n\ + /// \n\ + fn process_{property_formatted}_case(input: I, expected: O) {{\n\ + // typical implementation:\n\ + // assert_eq!(\n\ + // student_{property_formatted}_func(input),\n\ + // expected\n\ + // )\n unimplemented!()\n\ + }}\n\ + \n\ + ", + property = property, + property_formatted = format_exercise_property(property), + ) +} + +// Depending on the type of the item variable, +// transform item into corresponding Rust literal +fn into_literal(item: &Value, use_maplit: bool) -> String { + if item.is_string() { + format!("\"{}\"", item.as_str().unwrap()) + } else if item.is_array() { + format!( + "vec![{}]", + item.as_array() + .unwrap() + .iter() + .map(|item| into_literal(item, use_maplit)) + .collect::>() + .join(", ") + ) + } else if item.is_number() || item.is_boolean() || item.is_null() { + format!("{}", item) + } else if !use_maplit { + let key_values = item + .as_object() + .unwrap() + .iter() + .map(|(key, value)| { + format!( + "hm.insert(\"{}\", {});", + key, + into_literal(value, use_maplit) + ) + }).collect::(); + + format!( + "{{let mut hm = ::std::collections::HashMap::new(); {} hm}}", + key_values + ) + } else { + let key_values = item + .as_object() + .unwrap() + .iter() + .map(|(key, value)| format!("\"{}\"=>{}", key, into_literal(value, use_maplit))) + .collect::>() + .join(","); + + format!("hashmap!{{{}}}", key_values) + } +} + +pub fn generate_test_function(case: &Value, use_maplit: bool) -> String { + let description = case.get("description").unwrap().as_str().unwrap(); + + let property = case.get("property").unwrap().as_str().unwrap(); + + let comments = if let Some(comments) = case.get("comments") { + if comments.is_array() { + let comments_string = comments + .as_array() + .unwrap() + .iter() + .map(|line| format!("/// {}", line)) + .collect::(); + + format!("\n{}", comments_string) + } else { + format!("\n/// {}", comments.as_str().unwrap()) + } + } else { + "".to_string() + }; + + let input = into_literal(case.get("input").unwrap(), use_maplit); + + let expected = into_literal(case.get("expected").unwrap(), use_maplit); + + format!( + "#[test]\n\ + #[ignore]\n\ + /// {description}{comments}\n\ + fn test_{description_formatted}() {{\n\ + process_{property}_case({input}, {expected});\n\ + }}\n\ + \n\ + ", + description = description, + description_formatted = format_exercise_description(description), + property = format_exercise_property(property), + comments = comments, + input = input, + expected = expected + ) +} + +pub fn rustfmt(file_path: &Path) { + if let Ok(which_output) = Command::new("which").arg("rustfmt").output() { + if !String::from_utf8_lossy(&which_output.stdout) + .trim() + .is_empty() + {} + } + + let rustfmt_is_available = { + if let Some(path_var) = env::var_os("PATH") { + env::split_paths(&path_var) + .into_iter() + .any(|path| path.join("rustfmt").exists()) + } else { + false + } + }; + + if rustfmt_is_available { + Command::new("rustfmt") + .arg(file_path) + .output() + .expect("Failed to run rustfmt command on the test suite file"); + } +} + +pub fn exercise_exists(exercise_name: &str) -> bool { + Path::new(&*TRACK_ROOT) + .join("exercises") + .join(exercise_name) + .exists() +} + +// Update the version of the specified exercise in the Cargo.toml file according to the passed canonical data +pub fn update_cargo_toml_version(exercise_name: &str, canonical_data: &Value) { + let cargo_toml_path = Path::new(&*TRACK_ROOT) + .join("exercises") + .join(exercise_name) + .join("Cargo.toml"); + + let cargo_toml_content = fs::read_to_string(&cargo_toml_path).unwrap_or_else(|_| { + panic!( + "Failed to read the contents of the {} file", + cargo_toml_path.to_str().unwrap() + ) + }); + + let mut cargo_toml: TomlValue = cargo_toml_content.parse().unwrap(); + + { + let package_table = cargo_toml["package"].as_table_mut().unwrap(); + + package_table.insert( + "version".to_string(), + TomlValue::String(canonical_data["version"].as_str().unwrap().to_string()), + ); + } + + fs::write(&cargo_toml_path, cargo_toml.to_string()).unwrap_or_else(|_| { + panic!( + "Failed to update the contents of the {} file", + cargo_toml_path.to_str().unwrap() + ); + }); +}