From 13cdc1aa699f20bf7c20c67140ac90c6e99473e8 Mon Sep 17 00:00:00 2001 From: Roberto Vidal Date: Sat, 20 Jun 2020 11:11:45 +0200 Subject: [PATCH] feat: add fake manifest for analyzers/IDEs (fixes #443) --- .cargo/config | 2 + .gitignore | 1 + Cargo.lock | 66 ++++---- Cargo.toml | 8 +- README.md | 11 ++ exercises/Cargo.toml | 286 +++++++++++++++++++++++++++++++++++ src/main.rs | 29 +++- tests/integration_tests.rs | 20 --- tests/project_consistency.rs | 30 ++++ tooling/Cargo.toml | 11 ++ tooling/src/lib.rs | 79 ++++++++++ 11 files changed, 491 insertions(+), 52 deletions(-) create mode 100644 .cargo/config create mode 100644 exercises/Cargo.toml create mode 100644 tests/project_consistency.rs create mode 100644 tooling/Cargo.toml create mode 100644 tooling/src/lib.rs diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..d57ee26 --- /dev/null +++ b/.cargo/config @@ -0,0 +1,2 @@ +[alias] +generate-manifest = "run --features maintainer -- maintainer" diff --git a/.gitignore b/.gitignore index 06de871..af122cd 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ exercises/clippy/Cargo.toml exercises/clippy/Cargo.lock .idea .vscode +exercises/Cargo.lock diff --git a/Cargo.lock b/Cargo.lock index cdc1af3..5bcb426 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -134,7 +134,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.112 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -431,18 +431,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "0.4.30" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "quote" -version = "0.6.12" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -602,8 +602,9 @@ dependencies = [ "notify 4.0.15 (registry+https://github.com/rust-lang/crates.io-index)", "predicates 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.112 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tooling 0.1.0", ] [[package]] @@ -639,20 +640,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.92" +version = "1.0.112" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.112 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.92" +version = "1.0.112" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -662,7 +663,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.112 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -682,12 +683,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" -version = "0.15.34" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -727,10 +728,19 @@ dependencies = [ [[package]] name = "toml" -version = "0.4.10" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.112 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tooling" +version = "0.1.0" +dependencies = [ + "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.112 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -750,7 +760,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "unicode-xid" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -869,8 +879,8 @@ dependencies = [ "checksum predicates 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "53e09015b0d3f5a0ec2d4428f7559bb7b3fff341b4e159fedd1d57fac8b939ff" "checksum predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "06075c3a3e92559ff8929e7a280684489ea27fe44805174c3ebd9328dcb37178" "checksum predicates-tree 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8e63c4859013b38a76eca2414c64911fba30def9e3202ac461a2d22831220124" -"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" +"checksum proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" +"checksum quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" @@ -892,22 +902,22 @@ dependencies = [ "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)" = "32746bf0f26eab52f06af0d0aa1984f641341d06d8d673c693871da2d188c9be" -"checksum serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)" = "46a3223d0c9ba936b61c0d2e3e559e3217dbfb8d65d06d26e8b3c25de38bae3e" +"checksum serde 1.0.112 (registry+https://github.com/rust-lang/crates.io-index)" = "736aac72d1eafe8e5962d1d1c3d99b0df526015ba40915cb3c49d042e92ec243" +"checksum serde_derive 1.0.112 (registry+https://github.com/rust-lang/crates.io-index)" = "bf0343ce212ac0d3d6afd9391ac8e9c9efe06b533c8d33f660f6390cc4093f57" "checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c4488ae950c49d403731982257768f48fada354a5203fe81f9bb6f43ca9002be" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -"checksum syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)" = "a1393e4a97a19c01e900df2aec855a29f71cf02c402e2f443b8d2747c25c5dbe" +"checksum syn 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" "checksum termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dde0593aeb8d47accea5392b39350015b5eccb12c0d98044d856983d89548dea" "checksum termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72b620c5ea021d75a735c943269bb07d30c9b77d6ac6b236bc8b5c496ef05625" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" -"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" +"checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" "checksum treeline 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1" diff --git a/Cargo.toml b/Cargo.toml index 6814284..b4d86fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,14 +4,19 @@ version = "4.1.0" authors = ["Marisa ", "Carol (Nichols || Goulding) "] edition = "2018" +[features] +# Internal tooling for maintainers +maintainer = ["tooling"] + [dependencies] clap = "2.32.0" indicatif = "0.10.3" console = "0.7.7" notify = "4.0.15" -toml = "0.4.10" +toml = "0.5.6" regex = "1.1.6" serde = {version = "1.0.10", features = ["derive"]} +tooling = { path = "./tooling", optional = true } [[bin]] name = "rustlings" @@ -21,3 +26,4 @@ path = "src/main.rs" assert_cmd = "0.11.0" predicates = "1.0.1" glob = "0.3.0" +tooling = { path = "./tooling" } diff --git a/README.md b/README.md index a618abb..1210079 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,17 @@ exercise: rustlings hint myExercise1 ``` +### Using an IDE +**TL,DR**: open the `exercises/` folder in your editor. + +There are several editors and plugins that are able to analyze Rust code for a full blown IDE (or IDE-like) experience. The official language plugin of the Rust project is [rust-analyzer](rust-analyzer), but there are others, like [IntelliJ Rust](intellij) or [rls](rls). + +These tools usually rely on the default structure of a Cargo project to perform their analyses. Conversely, Rustlings is conceived as a collection of isolated exercises. Nevertheless, we auto-generate a "fake" project manifest in `exercises/Cargo.toml` that should bypass this mismatch. If you open the `exercises/` folder with your Rust-enabled editor, you should be able to enjoy its capabilities. + +[rust-analyzer]: https://rust-analyzer.github.io/ +[rls]: https://github.com/rust-lang/rls +[intellij]: https://intellij-rust.github.io/ + ## Testing yourself After every couple of sections, there will be a quiz that'll test your knowledge on a bunch of sections at once. These quizzes are found in `exercises/quizN.rs`. diff --git a/exercises/Cargo.toml b/exercises/Cargo.toml new file mode 100644 index 0000000..da3b7a8 --- /dev/null +++ b/exercises/Cargo.toml @@ -0,0 +1,286 @@ +# This Cargo.toml is AUTOGENERATED, you shouldn't need to edit it. +# The `rustling` commands do not rely on this manifest at all, its only purpose +# is to help editors analyze your code. +# To regenerate it, run `cargo generate-manifest`. +[package] +name = 'exercises' +version = '0.1.0' +edition = '2018' +authors = ['The Rustlings Maintainers'] +publish = false + +[[bin]] +name = 'clippy1' +path = 'clippy/clippy1.rs' + +[[bin]] +name = 'clippy2' +path = 'clippy/clippy2.rs' + +[[bin]] +name = 'as_ref_mut' +path = 'conversions/as_ref_mut.rs' + +[[bin]] +name = 'from_into' +path = 'conversions/from_into.rs' + +[[bin]] +name = 'from_str' +path = 'conversions/from_str.rs' + +[[bin]] +name = 'try_from_into' +path = 'conversions/try_from_into.rs' + +[[bin]] +name = 'using_as' +path = 'conversions/using_as.rs' + +[[bin]] +name = 'enums1' +path = 'enums/enums1.rs' + +[[bin]] +name = 'enums2' +path = 'enums/enums2.rs' + +[[bin]] +name = 'enums3' +path = 'enums/enums3.rs' + +[[bin]] +name = 'errors1' +path = 'error_handling/errors1.rs' + +[[bin]] +name = 'errors2' +path = 'error_handling/errors2.rs' + +[[bin]] +name = 'errors3' +path = 'error_handling/errors3.rs' + +[[bin]] +name = 'errorsn' +path = 'error_handling/errorsn.rs' + +[[bin]] +name = 'result1' +path = 'error_handling/result1.rs' + +[[bin]] +name = 'functions1' +path = 'functions/functions1.rs' + +[[bin]] +name = 'functions2' +path = 'functions/functions2.rs' + +[[bin]] +name = 'functions3' +path = 'functions/functions3.rs' + +[[bin]] +name = 'functions4' +path = 'functions/functions4.rs' + +[[bin]] +name = 'functions5' +path = 'functions/functions5.rs' + +[[bin]] +name = 'generics1' +path = 'generics/generics1.rs' + +[[bin]] +name = 'generics2' +path = 'generics/generics2.rs' + +[[bin]] +name = 'generics3' +path = 'generics/generics3.rs' + +[[bin]] +name = 'if1' +path = 'if/if1.rs' + +[[bin]] +name = 'if2' +path = 'if/if2.rs' + +[[bin]] +name = 'macros1' +path = 'macros/macros1.rs' + +[[bin]] +name = 'macros2' +path = 'macros/macros2.rs' + +[[bin]] +name = 'macros3' +path = 'macros/macros3.rs' + +[[bin]] +name = 'macros4' +path = 'macros/macros4.rs' + +[[bin]] +name = 'modules1' +path = 'modules/modules1.rs' + +[[bin]] +name = 'modules2' +path = 'modules/modules2.rs' + +[[bin]] +name = 'move_semantics1' +path = 'move_semantics/move_semantics1.rs' + +[[bin]] +name = 'move_semantics2' +path = 'move_semantics/move_semantics2.rs' + +[[bin]] +name = 'move_semantics3' +path = 'move_semantics/move_semantics3.rs' + +[[bin]] +name = 'move_semantics4' +path = 'move_semantics/move_semantics4.rs' + +[[bin]] +name = 'option1' +path = 'option/option1.rs' + +[[bin]] +name = 'option2' +path = 'option/option2.rs' + +[[bin]] +name = 'primitive_types1' +path = 'primitive_types/primitive_types1.rs' + +[[bin]] +name = 'primitive_types2' +path = 'primitive_types/primitive_types2.rs' + +[[bin]] +name = 'primitive_types3' +path = 'primitive_types/primitive_types3.rs' + +[[bin]] +name = 'primitive_types4' +path = 'primitive_types/primitive_types4.rs' + +[[bin]] +name = 'primitive_types5' +path = 'primitive_types/primitive_types5.rs' + +[[bin]] +name = 'primitive_types6' +path = 'primitive_types/primitive_types6.rs' + +[[bin]] +name = 'quiz1' +path = 'quiz1.rs' + +[[bin]] +name = 'quiz2' +path = 'quiz2.rs' + +[[bin]] +name = 'quiz3' +path = 'quiz3.rs' + +[[bin]] +name = 'quiz4' +path = 'quiz4.rs' + +[[bin]] +name = 'arc1' +path = 'standard_library_types/arc1.rs' + +[[bin]] +name = 'box1' +path = 'standard_library_types/box1.rs' + +[[bin]] +name = 'iterators2' +path = 'standard_library_types/iterators2.rs' + +[[bin]] +name = 'iterators3' +path = 'standard_library_types/iterators3.rs' + +[[bin]] +name = 'iterators4' +path = 'standard_library_types/iterators4.rs' + +[[bin]] +name = 'strings1' +path = 'strings/strings1.rs' + +[[bin]] +name = 'strings2' +path = 'strings/strings2.rs' + +[[bin]] +name = 'structs1' +path = 'structs/structs1.rs' + +[[bin]] +name = 'structs2' +path = 'structs/structs2.rs' + +[[bin]] +name = 'structs3' +path = 'structs/structs3.rs' + +[[bin]] +name = 'tests1' +path = 'tests/tests1.rs' + +[[bin]] +name = 'tests2' +path = 'tests/tests2.rs' + +[[bin]] +name = 'tests3' +path = 'tests/tests3.rs' + +[[bin]] +name = 'threads1' +path = 'threads/threads1.rs' + +[[bin]] +name = 'traits1' +path = 'traits/traits1.rs' + +[[bin]] +name = 'traits2' +path = 'traits/traits2.rs' + +[[bin]] +name = 'variables1' +path = 'variables/variables1.rs' + +[[bin]] +name = 'variables2' +path = 'variables/variables2.rs' + +[[bin]] +name = 'variables3' +path = 'variables/variables3.rs' + +[[bin]] +name = 'variables4' +path = 'variables/variables4.rs' + +[[bin]] +name = 'variables5' +path = 'variables/variables5.rs' + +[[bin]] +name = 'variables6' +path = 'variables/variables6.rs' diff --git a/src/main.rs b/src/main.rs index b5814bf..3b9ee2a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,7 +23,7 @@ mod run; mod verify; fn main() { - let matches = App::new("rustlings") + let app = App::new("rustlings") .version(crate_version!()) .author("Olivia Hugger, Carol Nichols") .about("Rustlings is a collection of small exercises to get you used to writing and reading Rust code") @@ -53,8 +53,18 @@ fn main() { .alias("h") .about("Returns a hint for the current exercise") .arg(Arg::with_name("name").required(true).index(1)), - ) - .get_matches(); + ); + + let matches; + #[cfg(feature = "maintainer")] + { + let app = app.subcommand(SubCommand::with_name("maintainer")); + matches = app.get_matches(); + } + #[cfg(not(feature = "maintainer"))] + { + matches = app.get_matches(); + } if matches.subcommand_name().is_none() { println!(); @@ -88,6 +98,13 @@ fn main() { let exercises = toml::from_str::(toml_str).unwrap().exercises; let verbose = matches.is_present("nocapture"); + #[cfg(feature = "maintainer")] + { + if matches.subcommand_matches("maintainer").is_some() { + maintainer(); + } + } + if let Some(ref matches) = matches.subcommand_matches("run") { let name = matches.value_of("name").unwrap(); @@ -217,3 +234,9 @@ fn rustc_exists() -> bool { .map(|status| status.success()) .unwrap_or(false) } + +#[cfg(feature = "maintainer")] +fn maintainer() { + let manifest = tooling::generate(&Path::new("exercises")); + fs::write("exercises/Cargo.toml", manifest).unwrap(); +} diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 2baf9b8..f82d3f5 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1,8 +1,5 @@ use assert_cmd::prelude::*; -use glob::glob; use predicates::boolean::PredicateBooleanExt; -use std::fs::File; -use std::io::Read; use std::process::Command; #[test] @@ -121,23 +118,6 @@ fn get_hint_for_single_test() { .stdout("Hello!\n"); } -#[test] -fn all_exercises_require_confirmation() { - for exercise in glob("exercises/**/*.rs").unwrap() { - let path = exercise.unwrap(); - let source = { - let mut file = File::open(&path).unwrap(); - let mut s = String::new(); - file.read_to_string(&mut s).unwrap(); - s - }; - source.matches("// I AM NOT DONE").next().expect(&format!( - "There should be an `I AM NOT DONE` annotation in {:?}", - path - )); - } -} - #[test] fn run_compile_exercise_does_not_prompt() { Command::cargo_bin("rustlings") diff --git a/tests/project_consistency.rs b/tests/project_consistency.rs new file mode 100644 index 0000000..dbb85b7 --- /dev/null +++ b/tests/project_consistency.rs @@ -0,0 +1,30 @@ +use glob::glob; +use std::fs; +use std::path::{Path, PathBuf}; + +#[test] +fn all_exercises_require_confirmation() { + for path in all_exercises() { + let source = fs::read_to_string(&path).unwrap(); + source.matches("// I AM NOT DONE").next().expect(&format!( + "There should be an `I AM NOT DONE` annotation in {:?}", + path + )); + } +} + +#[test] +fn cargo_toml_is_up_to_date() { + let exercises_manifest = fs::read_to_string("exercises/Cargo.toml").unwrap(); + let generated_manifest = tooling::generate(&Path::new("exercises")); + assert_eq!( + generated_manifest, exercises_manifest, + "exercises/Cargo.toml is not up to date, run `cargo generate-manifest`" + ); +} + +fn all_exercises() -> impl Iterator { + glob("exercises/**/*.rs") + .unwrap() + .map(|result| result.expect("Unable to traverse exercises folder")) +} diff --git a/tooling/Cargo.toml b/tooling/Cargo.toml new file mode 100644 index 0000000..fe3646d --- /dev/null +++ b/tooling/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "tooling" +version = "0.1.0" +authors = ["The Rustlings Maintainers"] +edition = "2018" +publish = false + +[dependencies] +glob = "0.3.0" +serde = { version = "1.0.112", feature = ["derive"] } +toml = "0.5.6" diff --git a/tooling/src/lib.rs b/tooling/src/lib.rs new file mode 100644 index 0000000..d7e23a8 --- /dev/null +++ b/tooling/src/lib.rs @@ -0,0 +1,79 @@ +use glob::glob; +use serde::Serialize; +use std::path::Path; +use toml; + +pub fn generate(root: &Path) -> String { + let mut bins = vec![]; + + let root_glob = { + let mut p = root.to_owned(); + p.push("**"); + p.push("*.rs"); + p + }; + + let mut exercise_paths: Vec<_> = glob(root_glob.to_str().unwrap()) + .unwrap() + .collect::>() + .expect("Error traversing root"); + + exercise_paths.sort(); + + for path in &exercise_paths { + let name = path + .file_stem() + .expect("Malformed exercise path") + .to_str() + .expect("Invalid UTF8 in exercise path") + .to_string(); + + let target_path = path.strip_prefix(root).unwrap(); + + bins.push(Bin { + name, + path: target_path.to_str().unwrap().to_string(), + }); + } + + let manifest = Manifest { + package: Package { + name: "exercises", + version: "0.1.0", + authors: &["The Rustlings Maintainers"], + edition: "2018", + publish: false, + }, + bin: bins, + }; + + format!( + "# This Cargo.toml is AUTOGENERATED, you shouldn't need to edit it.\n\ + # The `rustling` commands do not rely on this manifest at all, its only purpose\n\ + # is to help editors analyze your code.\n\ + # To regenerate it, run `cargo generate-manifest`.\n\ + {}", + toml::ser::to_string_pretty(&manifest).expect("Invalid toml") + ) +} + +#[derive(Serialize)] +struct Manifest { + package: Package, + bin: Vec, +} + +#[derive(Serialize)] +struct Package { + name: &'static str, + version: &'static str, + edition: &'static str, + authors: &'static [&'static str], + publish: bool, +} + +#[derive(Serialize)] +struct Bin { + name: String, + path: String, +}