mirror of
https://github.com/rust-lang/rustlings.git
synced 2025-12-26 00:11:49 +02:00
Compare commits
81 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7fca1d28af | ||
|
|
9c3d765403 | ||
|
|
bb652ceb91 | ||
|
|
62696f5819 | ||
|
|
41170ce341 | ||
|
|
e2092a4ddd | ||
|
|
fd1441d122 | ||
|
|
e1422c6443 | ||
|
|
1f07fd4150 | ||
|
|
9b92aa08ae | ||
|
|
8bf8cbbd61 | ||
|
|
f507844102 | ||
|
|
fffbb60ed9 | ||
|
|
9aec4abc4d | ||
|
|
a53b3f199f | ||
|
|
d6d696b66a | ||
|
|
ca6bf966dd | ||
|
|
5a9f8860ca | ||
|
|
5423bc66a9 | ||
|
|
187d2ad226 | ||
|
|
7cf0d5d15e | ||
|
|
1f2ee8cb62 | ||
|
|
35c3d0b3fc | ||
|
|
0279294972 | ||
|
|
7eddee6f7a | ||
|
|
f2c48cfac5 | ||
|
|
6ae0a00211 | ||
|
|
bfcf38c8bc | ||
|
|
9e328da641 | ||
|
|
e336d04c79 | ||
|
|
a71bc62c29 | ||
|
|
4b0b7093e5 | ||
|
|
8387de64d3 | ||
|
|
77de6e5d6a | ||
|
|
8c867a001a | ||
|
|
d01a71f7de | ||
|
|
04d1d4c00e | ||
|
|
d7e58ee1af | ||
|
|
ffb165ce26 | ||
|
|
65cb09eb2e | ||
|
|
78552ebd7a | ||
|
|
0c7bd12372 | ||
|
|
3d11d7685b | ||
|
|
592ae6b4d2 | ||
|
|
4fa79ee02f | ||
|
|
fbd0ccbd5b | ||
|
|
8c008a0e7d | ||
|
|
11fe19d08a | ||
|
|
1b3469f236 | ||
|
|
022921168d | ||
|
|
c6765eb3eb | ||
|
|
c5a374fbf2 | ||
|
|
f3ee70489f | ||
|
|
6a27ba735c | ||
|
|
91dce31265 | ||
|
|
040ca18a64 | ||
|
|
f43cb124f6 | ||
|
|
11875aed6e | ||
|
|
f07703eb7a | ||
|
|
fd4eda8bda | ||
|
|
bf8d927ab2 | ||
|
|
9fc4a83987 | ||
|
|
63280ed9e4 | ||
|
|
25f9d61410 | ||
|
|
c1f4257a91 | ||
|
|
8f9d7ce3d8 | ||
|
|
3b5dfac44e | ||
|
|
a6a8b61b12 | ||
|
|
6cd42bb821 | ||
|
|
4d7ce6e571 | ||
|
|
3f114cc069 | ||
|
|
58ccd72aff | ||
|
|
abf175111d | ||
|
|
9144c816bf | ||
|
|
999601d828 | ||
|
|
10d4d61d19 | ||
|
|
70e59cca3c | ||
|
|
7d6e2812fb | ||
|
|
05e8f02d0a | ||
|
|
9a14d72f08 | ||
|
|
04d0f78a2c |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,4 @@
|
||||
*.swp
|
||||
target/
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
.DS_Store
|
||||
|
||||
11
.travis.yml
Normal file
11
.travis.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
language: rust
|
||||
rust:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
script: cargo test --verbose
|
||||
matrix:
|
||||
allow_failures:
|
||||
- rust: nightly
|
||||
fast_finish: true
|
||||
cache: cargo
|
||||
1152
Cargo.lock
generated
Normal file
1152
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
14
Cargo.toml
14
Cargo.toml
@@ -1,12 +1,20 @@
|
||||
[package]
|
||||
name = "rustlings"
|
||||
version = "1.0.0"
|
||||
authors = ["Lynn <819880950@qq.com>", "Carol (Nichols || Goulding) <carol.nichols@gmail.com"]
|
||||
version = "1.3.0"
|
||||
authors = ["Olivia <819880950@qq.com>", "Carol (Nichols || Goulding) <carol.nichols@gmail.com"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.32.0"
|
||||
indicatif = "0.9.0"
|
||||
console = "0.6.2"
|
||||
syntect = "3.0.2"
|
||||
notify = "4.0.0"
|
||||
toml = "0.4.10"
|
||||
serde = {version = "1.0.10", features = ["derive"]}
|
||||
|
||||
[[bin]]
|
||||
name = "rustlings"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = "0.11.0"
|
||||
|
||||
55
README.md
55
README.md
@@ -13,27 +13,36 @@ Alternatively, for a first-time Rust learner, there's several other resources:
|
||||
|
||||
## Getting Started
|
||||
|
||||
To use `rustlings` you need to have [Rust](https://www.rust-lang.org/) installed on your computer. To install Rust, go to [rustup.rs](https://rustup.rs/).
|
||||
|
||||
Once Rust is installed, clone the `rustlings` repository and enter the resulting directory:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/rust-lang/rustlings.git
|
||||
cd rustlings
|
||||
```
|
||||
|
||||
_Note: If you're on MacOS, make sure you've installed Xcode and its developer tools by typing `xcode-select --install`._
|
||||
|
||||
_Note: If you have Xcode 10+ installed, you also need to install the package file found at `/Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg`._
|
||||
|
||||
Once in the directory you can install `rustlings` on your machine and run the introduction:
|
||||
You will need to have Rust installed. You can get it by visiting https://rustup.rs. This'll also install Cargo, Rust's package/project manager.
|
||||
|
||||
## MacOS/Linux
|
||||
|
||||
Just run:
|
||||
|
||||
```bash
|
||||
cargo install --path .
|
||||
rustlings
|
||||
curl -L https://git.io/rustlings | bash
|
||||
# Or if you want it to be installed to a different path:
|
||||
curl -L https://git.io/rustlings | bash -s mypath/
|
||||
```
|
||||
|
||||
If you choose to not install the `rustlings` command, just replace `rustlings` with `cargo run` in the rest of this text.
|
||||
This will install Rustlings and give you access to the `rustlings` command. Run it to get started!
|
||||
|
||||
## Windows/Manually
|
||||
|
||||
Basically: Clone the repository, checkout to the latest tag, run `cargo install`.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/rust-lang/rustlings
|
||||
cd rustlings
|
||||
git checkout tags/1.0.0 # or whatever the latest version is (find out at https://github.com/rust-lang/rustlings/releases/latest)
|
||||
cargo install --force --path .
|
||||
```
|
||||
|
||||
Same as above, run `rustlings` to get started.
|
||||
|
||||
## Doing exercises
|
||||
|
||||
@@ -59,12 +68,6 @@ In case you want to go by your own order, or want to only verify a single exerci
|
||||
rustlings run exercises/path/to/exercise.rs
|
||||
```
|
||||
|
||||
Or if it's a `#[test]`:
|
||||
|
||||
```bash
|
||||
rustlings run --test exercises/path/to/exercise_with_test.rs
|
||||
```
|
||||
|
||||
In case you get stuck, there is usually a hint at the bottom of each exercise.
|
||||
|
||||
## Testing yourself
|
||||
@@ -96,18 +99,20 @@ If you are interested in improving or adding new ones, please feel free to contr
|
||||
First step is to add the exercise! Call it `exercises/yourTopic/yourTopicN.rs`, make sure to
|
||||
put in some helpful links, and link to sections of the book in `exercises/yourTopic/README.md`.
|
||||
|
||||
Then, you'll want to make sure it gets verified when you run `rustlings verify`. Open `src/verify.rs` and
|
||||
put your exercise somewhere in there:
|
||||
Next you want to make sure it runs when using `rustlings`. All exercises are stored in `info.toml`, under the `exercises` array. They're ordered by the order they're ran when using `rustlings verify`.
|
||||
|
||||
You want to make sure where in the file you add your exercise. If you're not sure, add it at the bottom and ask in your pull request. To add an exercise, edit the file like this:
|
||||
```diff
|
||||
...
|
||||
compile_only("exercises/functions5.rs")?;
|
||||
+ compile_only("exercises/yourTopic/yourTopicN.rs")?;
|
||||
compile_only("exercises/test1.rs")?;
|
||||
+ [[exercises]]
|
||||
+ path = "exercises/yourTopic/yourTopicN.rs"
|
||||
+ mode = "compile"
|
||||
...
|
||||
```
|
||||
|
||||
That's all!
|
||||
The `mode` attribute decides whether Rustlings will only compile your exercise, or compile and test it. If you have tests to verify in your exercise, choose `test`, otherwise `compile`.
|
||||
|
||||
That's all! Feel free to put up a pull request.
|
||||
|
||||
### Working on the source code
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Thanks for installing `rustlings`!
|
||||
Thanks for installing Rustlings!
|
||||
|
||||
## Is this your first time?
|
||||
Is this your first time?
|
||||
|
||||
Let's make sure you're up to speed:
|
||||
- You have Rust installed, preferably via `rustup`
|
||||
@@ -9,9 +9,7 @@ Let's make sure you're up to speed:
|
||||
- You have installed Rust language support for your editor
|
||||
- You have locally installed the `rustlings` command by running:
|
||||
|
||||
```sh
|
||||
cargo install --path .
|
||||
```
|
||||
|
||||
If you've done all of this (or even most of it), congrats! You're ready
|
||||
to start working with Rust.
|
||||
@@ -1,5 +1,5 @@
|
||||
For this exercise check out the sections:
|
||||
- [Error Handling](https://doc.rust-lang.org/book/2018-edition/ch09-02-recoverable-errors-with-result.html)
|
||||
- [Generics](https://doc.rust-lang.org/book/2018-edition/ch10-01-syntax.html)
|
||||
- [Error Handling](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html)
|
||||
- [Generics](https://doc.rust-lang.org/book/ch10-01-syntax.html)
|
||||
|
||||
of the Rust Book.
|
||||
of the Rust Book.
|
||||
|
||||
2
exercises/error_handling/errors1.rs
Executable file → Normal file
2
exercises/error_handling/errors1.rs
Executable file → Normal file
@@ -63,7 +63,7 @@ mod tests {
|
||||
// `Option`.
|
||||
|
||||
// To make this change, you'll need to:
|
||||
// - update the return type in the function signature to be a Result that
|
||||
// - update the return type in the function signature to be a Result<String, String> that
|
||||
// could be the variants `Ok(String)` and `Err(String)`
|
||||
// - change the body of the function to return `Ok(stuff)` where it currently
|
||||
// returns `Some(stuff)`
|
||||
|
||||
7
exercises/error_handling/errors2.rs
Executable file → Normal file
7
exercises/error_handling/errors2.rs
Executable file → Normal file
@@ -32,10 +32,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn item_quantity_is_a_valid_number() {
|
||||
assert_eq!(
|
||||
total_cost("34"),
|
||||
Ok(171)
|
||||
);
|
||||
assert_eq!(total_cost("34"), Ok(171));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -68,5 +65,5 @@ mod tests {
|
||||
// `Err(something)`. This pattern is very common in Rust, though, so there's
|
||||
// a `?` operator that does pretty much what you would make that match statement
|
||||
// do for you! Take a look at this section of the Error Handling chapter:
|
||||
// https://doc.rust-lang.org/stable/book/second-edition/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator
|
||||
// https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator
|
||||
// and give it a try!
|
||||
|
||||
0
exercises/error_handling/errors3.rs
Executable file → Normal file
0
exercises/error_handling/errors3.rs
Executable file → Normal file
115
exercises/error_handling/errorsn.rs
Executable file → Normal file
115
exercises/error_handling/errorsn.rs
Executable file → Normal file
@@ -65,7 +65,7 @@ fn test_ioerror() {
|
||||
assert_eq!("uh-oh!", read_and_validate(&mut b).unwrap_err().to_string());
|
||||
}
|
||||
|
||||
#[derive(PartialEq,Debug)]
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct PositiveNonzeroInteger(u64);
|
||||
|
||||
impl PositiveNonzeroInteger {
|
||||
@@ -83,11 +83,14 @@ impl PositiveNonzeroInteger {
|
||||
#[test]
|
||||
fn test_positive_nonzero_integer_creation() {
|
||||
assert!(PositiveNonzeroInteger::new(10).is_ok());
|
||||
assert_eq!(Err(CreationError::Negative), PositiveNonzeroInteger::new(-10));
|
||||
assert_eq!(
|
||||
Err(CreationError::Negative),
|
||||
PositiveNonzeroInteger::new(-10)
|
||||
);
|
||||
assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0));
|
||||
}
|
||||
|
||||
#[derive(PartialEq,Debug)]
|
||||
#[derive(PartialEq, Debug)]
|
||||
enum CreationError {
|
||||
Negative,
|
||||
Zero,
|
||||
@@ -108,16 +111,84 @@ impl error::Error for CreationError {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// First hint: To figure out what type should go where the ??? is, take a look
|
||||
// at the test helper function `test_with_str`, since it returns whatever
|
||||
// `read_and_validate` returns and`test_with_str` has its signature fully
|
||||
// specified.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Next hint: There are three places in `read_and_validate` that we call a
|
||||
// function that returns a `Result` (that is, the functions might fail).
|
||||
// Apply the `?` operator on those calls so that we return immediately from
|
||||
// `read_and_validate` if those function calls fail.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Another hint: under the hood, the `?` operator calls `From::from`
|
||||
// on the error value to convert it to a boxed trait object, a Box<error::Error>,
|
||||
// which is polymorphic-- that means that lots of different kinds of errors
|
||||
@@ -126,12 +197,50 @@ impl error::Error for CreationError {
|
||||
// Check out this section of the book:
|
||||
// https://doc.rust-lang.org/stable/book/second-edition/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Another another hint: Note that because the `?` operator returns
|
||||
// the *unwrapped* value in the `Ok` case, if we want to return a `Result` from
|
||||
// `read_and_validate` for *its* success case, we'll have to rewrap a value
|
||||
// that we got from the return value of a `?`ed call in an `Ok`-- this will
|
||||
// look like `Ok(something)`.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Another another another hint: `Result`s must be "used", that is, you'll
|
||||
// get a warning if you don't handle a `Result` that you get in your
|
||||
// function. Read more about that in the `std::result` module docs:
|
||||
|
||||
5
exercises/error_handling/option1.rs
Executable file → Normal file
5
exercises/error_handling/option1.rs
Executable file → Normal file
@@ -11,7 +11,10 @@ fn main() {
|
||||
println!("The last item in the list is {:?}", last);
|
||||
|
||||
let second_to_last = list.pop().unwrap();
|
||||
println!("The second-to-last item in the list is {:?}", second_to_last);
|
||||
println!(
|
||||
"The second-to-last item in the list is {:?}",
|
||||
second_to_last
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
9
exercises/error_handling/result1.rs
Executable file → Normal file
9
exercises/error_handling/result1.rs
Executable file → Normal file
@@ -1,10 +1,10 @@
|
||||
// result1.rs
|
||||
// Make this test pass! Scroll down for hints :)
|
||||
|
||||
#[derive(PartialEq,Debug)]
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct PositiveNonzeroInteger(u64);
|
||||
|
||||
#[derive(PartialEq,Debug)]
|
||||
#[derive(PartialEq, Debug)]
|
||||
enum CreationError {
|
||||
Negative,
|
||||
Zero,
|
||||
@@ -19,7 +19,10 @@ impl PositiveNonzeroInteger {
|
||||
#[test]
|
||||
fn test_creation() {
|
||||
assert!(PositiveNonzeroInteger::new(10).is_ok());
|
||||
assert_eq!(Err(CreationError::Negative), PositiveNonzeroInteger::new(-10));
|
||||
assert_eq!(
|
||||
Err(CreationError::Negative),
|
||||
PositiveNonzeroInteger::new(-10)
|
||||
);
|
||||
assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0));
|
||||
}
|
||||
|
||||
|
||||
0
exercises/functions/functions1.rs
Executable file → Normal file
0
exercises/functions/functions1.rs
Executable file → Normal file
0
exercises/functions/functions2.rs
Executable file → Normal file
0
exercises/functions/functions2.rs
Executable file → Normal file
0
exercises/functions/functions3.rs
Executable file → Normal file
0
exercises/functions/functions3.rs
Executable file → Normal file
0
exercises/functions/functions4.rs
Executable file → Normal file
0
exercises/functions/functions4.rs
Executable file → Normal file
0
exercises/functions/functions5.rs
Executable file → Normal file
0
exercises/functions/functions5.rs
Executable file → Normal file
0
exercises/if/if1.rs
Executable file → Normal file
0
exercises/if/if1.rs
Executable file → Normal file
0
exercises/macros/macros1.rs
Executable file → Normal file
0
exercises/macros/macros1.rs
Executable file → Normal file
0
exercises/macros/macros2.rs
Executable file → Normal file
0
exercises/macros/macros2.rs
Executable file → Normal file
0
exercises/macros/macros3.rs
Executable file → Normal file
0
exercises/macros/macros3.rs
Executable file → Normal file
0
exercises/macros/macros4.rs
Executable file → Normal file
0
exercises/macros/macros4.rs
Executable file → Normal file
@@ -4,4 +4,4 @@ In this section we'll give you an introduction to Rust's module system.
|
||||
|
||||
#### Book Sections
|
||||
|
||||
- [The Module System](https://doc.rust-lang.org/stable/book/ch07-02-modules-and-use-to-control-scope-and-privacy.html)
|
||||
- [The Module System](https://doc.rust-lang.org/stable/book/ch07-02-defining-modules-to-control-scope-and-privacy.html)
|
||||
|
||||
0
exercises/modules/modules1.rs
Executable file → Normal file
0
exercises/modules/modules1.rs
Executable file → Normal file
0
exercises/modules/modules2.rs
Executable file → Normal file
0
exercises/modules/modules2.rs
Executable file → Normal file
0
exercises/move_semantics/move_semantics1.rs
Executable file → Normal file
0
exercises/move_semantics/move_semantics1.rs
Executable file → Normal file
0
exercises/move_semantics/move_semantics2.rs
Executable file → Normal file
0
exercises/move_semantics/move_semantics2.rs
Executable file → Normal file
0
exercises/move_semantics/move_semantics3.rs
Executable file → Normal file
0
exercises/move_semantics/move_semantics3.rs
Executable file → Normal file
3
exercises/move_semantics/move_semantics4.rs
Executable file → Normal file
3
exercises/move_semantics/move_semantics4.rs
Executable file → Normal file
@@ -16,7 +16,8 @@ fn main() {
|
||||
|
||||
}
|
||||
|
||||
fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
|
||||
// `fill_vec()` no longer take `vec: Vec<i32>` as argument
|
||||
fn fill_vec() -> Vec<i32> {
|
||||
let mut vec = vec;
|
||||
|
||||
vec.push(22);
|
||||
|
||||
0
exercises/primitive_types/primitive_types1.rs
Executable file → Normal file
0
exercises/primitive_types/primitive_types1.rs
Executable file → Normal file
0
exercises/primitive_types/primitive_types2.rs
Executable file → Normal file
0
exercises/primitive_types/primitive_types2.rs
Executable file → Normal file
0
exercises/primitive_types/primitive_types3.rs
Executable file → Normal file
0
exercises/primitive_types/primitive_types3.rs
Executable file → Normal file
0
exercises/primitive_types/primitive_types4.rs
Executable file → Normal file
0
exercises/primitive_types/primitive_types4.rs
Executable file → Normal file
0
exercises/primitive_types/primitive_types5.rs
Executable file → Normal file
0
exercises/primitive_types/primitive_types5.rs
Executable file → Normal file
0
exercises/primitive_types/primitive_types6.rs
Executable file → Normal file
0
exercises/primitive_types/primitive_types6.rs
Executable file → Normal file
@@ -1,5 +1,5 @@
|
||||
For the Arc exercise check out the chapter [Shared-State Concurrency](https://doc.rust-lang.org/book/2018-edition/ch16-03-shared-state.html) of the Rust Book.
|
||||
For the Arc exercise check out the chapter [Shared-State Concurrency](https://doc.rust-lang.org/book/ch16-03-shared-state.html) of the Rust Book.
|
||||
|
||||
For the Iterator exercise check out the chapters [Iterator](https://doc.rust-lang.org/book/2018-edition/ch13-02-iterators.html) of the Rust Book and the [Iterator documentation](https://doc.rust-lang.org/stable/std/iter/trait.Iterator.htmlj).
|
||||
For the Iterator exercise check out the chapters [Iterator](https://doc.rust-lang.org/book/ch13-02-iterators.html) of the Rust Book and the [Iterator documentation](https://doc.rust-lang.org/stable/std/iter/).
|
||||
Do not adjust your monitors-- iterators 1 and 2 are indeed missing. Iterator 3 is a bit challenging so we're leaving space for some exercises to lead up to it!
|
||||
|
||||
|
||||
0
exercises/standard_library_types/arc1.rs
Executable file → Normal file
0
exercises/standard_library_types/arc1.rs
Executable file → Normal file
4
exercises/standard_library_types/iterator3.rs
Executable file → Normal file
4
exercises/standard_library_types/iterator3.rs
Executable file → Normal file
@@ -114,7 +114,7 @@ mod tests {
|
||||
|
||||
|
||||
|
||||
// Minor hint: In each of the two cases in the match in main, you can create x with either
|
||||
// Minor hint: In each of the two cases in the match in main, you can create x with either
|
||||
// a 'turbofish' or by hinting the type of x to the compiler. You may try both.
|
||||
|
||||
|
||||
@@ -143,5 +143,5 @@ mod tests {
|
||||
|
||||
|
||||
|
||||
// Major hint: Have a look at the Iter trait and at the explanation of its collect function.
|
||||
// Major hint: Have a look at the Iter trait and at the explanation of its collect function.
|
||||
// Especially the part about Result is interesting.
|
||||
|
||||
0
exercises/standard_library_types/iterators4.rs
Executable file → Normal file
0
exercises/standard_library_types/iterators4.rs
Executable file → Normal file
0
exercises/strings/strings1.rs
Executable file → Normal file
0
exercises/strings/strings1.rs
Executable file → Normal file
0
exercises/strings/strings2.rs
Executable file → Normal file
0
exercises/strings/strings2.rs
Executable file → Normal file
7
exercises/structs/README.md
Normal file
7
exercises/structs/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
### Strings
|
||||
|
||||
Rust has three struct types: a classic c struct, a tuple struct, and a unit struct.
|
||||
|
||||
#### Book Sections
|
||||
|
||||
- [Structures](https://doc.rust-lang.org/rust-by-example/custom_types/structs.html)
|
||||
46
exercises/structs/structs1.rs
Normal file
46
exercises/structs/structs1.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
// structs1.rs
|
||||
// Address all the TODOs to make the tests pass!
|
||||
|
||||
struct ColorClassicStruct {
|
||||
// TODO: Something goes here
|
||||
}
|
||||
|
||||
struct ColorTupleStruct(/* TODO: Something goes here */);
|
||||
|
||||
struct ColorUnitStruct;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn classic_c_structs() {
|
||||
// TODO: Instantiate a classic c struct!
|
||||
// let green =
|
||||
|
||||
assert_eq!(green.name, "green");
|
||||
assert_eq!(green.hex, "#00FF00");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tuple_structs() {
|
||||
// TODO: Instantiate a tuple struct!
|
||||
// For more fun, use the field initialization shorthand.
|
||||
// let green =
|
||||
|
||||
assert_eq!(green.0, "green");
|
||||
assert_eq!(green.1, "#00FF00");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unit_structs() {
|
||||
// TODO: Instantiate a unit struct!
|
||||
// let green =
|
||||
|
||||
if let ColorUnitStruct = green {
|
||||
assert!(true);
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
7
exercises/test2.rs
Executable file → Normal file
7
exercises/test2.rs
Executable file → Normal file
@@ -17,6 +17,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn returns_twice_of_positive_numbers() {
|
||||
assert_eq!(4, 4);
|
||||
assert_eq!(times_two(4), ???);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_twice_of_negative_numbers() {
|
||||
// TODO write an assert for `times_two(-4)`
|
||||
}
|
||||
}
|
||||
|
||||
0
exercises/test3.rs
Executable file → Normal file
0
exercises/test3.rs
Executable file → Normal file
0
exercises/tests/tests1.rs
Executable file → Normal file
0
exercises/tests/tests1.rs
Executable file → Normal file
0
exercises/tests/tests2.rs
Executable file → Normal file
0
exercises/tests/tests2.rs
Executable file → Normal file
0
exercises/tests/tests3.rs
Executable file → Normal file
0
exercises/tests/tests3.rs
Executable file → Normal file
@@ -1 +1 @@
|
||||
For this exercise check out the [Dining Philosophers example](https://doc.rust-lang.org/1.4.0/book/dining-philosophers.html) and the chapter [Concurrency](https://doc.rust-lang.org/book/2018-edition/ch16-01-threads.html) of the Rust Book.
|
||||
For this exercise check out the [Dining Philosophers example](https://doc.rust-lang.org/1.4.0/book/dining-philosophers.html) and the chapter [Concurrency](https://doc.rust-lang.org/book/ch16-01-threads.html) of the Rust Book.
|
||||
0
exercises/threads/threads1.rs
Executable file → Normal file
0
exercises/threads/threads1.rs
Executable file → Normal file
0
exercises/variables/variables1.rs
Executable file → Normal file
0
exercises/variables/variables1.rs
Executable file → Normal file
0
exercises/variables/variables2.rs
Executable file → Normal file
0
exercises/variables/variables2.rs
Executable file → Normal file
0
exercises/variables/variables3.rs
Executable file → Normal file
0
exercises/variables/variables3.rs
Executable file → Normal file
0
exercises/variables/variables4.rs
Executable file → Normal file
0
exercises/variables/variables4.rs
Executable file → Normal file
219
info.toml
Normal file
219
info.toml
Normal file
@@ -0,0 +1,219 @@
|
||||
# VARIABLES
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/variables/variables1.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/variables/variables2.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/variables/variables3.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/variables/variables4.rs"
|
||||
mode = "compile"
|
||||
|
||||
# IF
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/if/if1.rs"
|
||||
mode = "test"
|
||||
|
||||
# FUNCTIONS
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/functions/functions1.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/functions/functions2.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/functions/functions3.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/functions/functions4.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/functions/functions5.rs"
|
||||
mode = "compile"
|
||||
|
||||
# TEST 1
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/test1.rs"
|
||||
mode = "test"
|
||||
|
||||
# PRIMITIVE TYPES
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/primitive_types/primitive_types1.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/primitive_types/primitive_types2.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/primitive_types/primitive_types3.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/primitive_types/primitive_types4.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/primitive_types/primitive_types5.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/primitive_types/primitive_types6.rs"
|
||||
mode = "compile"
|
||||
|
||||
# STRUCTS
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/structs/structs1.rs"
|
||||
mode = "test"
|
||||
|
||||
# TESTS
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/tests/tests1.rs"
|
||||
mode = "test"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/tests/tests2.rs"
|
||||
mode = "test"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/tests/tests3.rs"
|
||||
mode = "test"
|
||||
|
||||
# TEST 2
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/test2.rs"
|
||||
mode = "test"
|
||||
|
||||
# STRINGS
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/strings/strings1.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/strings/strings2.rs"
|
||||
mode = "compile"
|
||||
|
||||
# TEST 3
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/test3.rs"
|
||||
mode = "compile"
|
||||
|
||||
# MODULES
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/modules/modules1.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/modules/modules2.rs"
|
||||
mode = "compile"
|
||||
|
||||
# MACROS
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/macros/macros1.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/macros/macros2.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/macros/macros3.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/macros/macros4.rs"
|
||||
mode = "compile"
|
||||
|
||||
# TEST 4
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/test4.rs"
|
||||
mode = "compile"
|
||||
|
||||
# MOVE SEMANTICS
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/move_semantics/move_semantics1.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/move_semantics/move_semantics2.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/move_semantics/move_semantics3.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/move_semantics/move_semantics4.rs"
|
||||
mode = "compile"
|
||||
|
||||
# ERROR HANDLING
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/error_handling/errors1.rs"
|
||||
mode = "test"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/error_handling/errors2.rs"
|
||||
mode = "test"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/error_handling/errors3.rs"
|
||||
mode = "test"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/error_handling/errorsn.rs"
|
||||
mode = "test"
|
||||
|
||||
# OPTIONS / RESULTS
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/error_handling/option1.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/error_handling/result1.rs"
|
||||
mode = "test"
|
||||
|
||||
# THREADS
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/threads/threads1.rs"
|
||||
mode = "compile"
|
||||
|
||||
# STANDARD LIBRARY TYPES
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/standard_library_types/arc1.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/standard_library_types/iterator3.rs"
|
||||
mode = "test"
|
||||
|
||||
[[exercises]]
|
||||
path = "exercises/standard_library_types/iterators4.rs"
|
||||
mode = "test"
|
||||
103
install.sh
Executable file
103
install.sh
Executable file
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "Let's get you set up with Rustlings!"
|
||||
|
||||
echo "Checking requirements..."
|
||||
if [ -x "$(command -v git)" ]
|
||||
then
|
||||
echo "SUCCESS: Git is installed"
|
||||
else
|
||||
echo "WARNING: Git does not seem to be installed."
|
||||
echo "Please download Git using your package manager or over https://git-scm.com/!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -x "$(command -v rustc)" ]
|
||||
then
|
||||
echo "SUCCESS: Rust is installed"
|
||||
else
|
||||
echo "WARNING: Rust does not seem to be installed."
|
||||
echo "Please download Rust using https://rustup.rs!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -x "$(command -v cargo)" ]
|
||||
then
|
||||
echo "SUCCESS: Cargo is installed"
|
||||
else
|
||||
echo "WARNING: Cargo does not seem to be installed."
|
||||
echo "Please download Rust and Cargo using https://rustup.rs!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Function that compares two versions strings v1 and v2 given in arguments (e.g 1.31 and 1.33.0).
|
||||
# Returns 1 if v1 > v2, 0 if v1 == v2, 2 if v1 < v2.
|
||||
function vercomp() {
|
||||
if [[ $1 == $2 ]]
|
||||
then
|
||||
return 0
|
||||
fi
|
||||
v1=( ${1//./ } )
|
||||
v2=( ${2//./ } )
|
||||
len1=${#v1[@]}
|
||||
len2=${#v2[@]}
|
||||
max_len=$len1
|
||||
if [[ $max_len -lt $len2 ]]
|
||||
then
|
||||
max_len=$len2
|
||||
fi
|
||||
for i in `seq 0 $max_len`
|
||||
do
|
||||
# Fill empty fields with zeros in v1
|
||||
if [ -z "${v1[$i]}" ]
|
||||
then
|
||||
v1[$i]=0
|
||||
fi
|
||||
# And in v2
|
||||
if [ -z "${v2[$i]}" ]
|
||||
then
|
||||
v2[$i]=0
|
||||
fi
|
||||
if [ ${v1[$i]} -gt ${v2[$i]} ]
|
||||
then
|
||||
return 1
|
||||
fi
|
||||
if [ ${v1[$i]} -lt ${v2[$i]} ]
|
||||
then
|
||||
return 2
|
||||
fi
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
RustVersion=$(rustc --version | cut -d " " -f 2)
|
||||
MinRustVersion=1.31
|
||||
vercomp $RustVersion $MinRustVersion
|
||||
if [ $? -eq 2 ]
|
||||
then
|
||||
echo "WARNING: Rust version is too old: $RustVersion - needs at least $MinRustVersion"
|
||||
echo "Please update Rust with 'rustup update'"
|
||||
exit 1
|
||||
else
|
||||
echo "SUCCESS: Rust is up to date"
|
||||
fi
|
||||
|
||||
Path=${1:-rustlings/}
|
||||
echo "Cloning Rustlings at $Path..."
|
||||
git clone -q https://github.com/rust-lang/rustlings $Path
|
||||
|
||||
Version=$(curl -s https://api.github.com/repos/rust-lang/rustlings/releases/latest | python -c "import json,sys;obj=json.load(sys.stdin);print(obj['tag_name']);")
|
||||
echo "Checking out version $Version..."
|
||||
cd $Path
|
||||
git checkout -q tags/$Version
|
||||
|
||||
echo "Installing the 'rustlings' executable..."
|
||||
cargo install --force --path .
|
||||
|
||||
if [ -x "$(rustlings)" ]
|
||||
then
|
||||
echo "WARNING: Please check that you have '~/.cargo/bin' in your PATH environment variable!"
|
||||
fi
|
||||
|
||||
echo "All done! Run 'rustlings' to get started."
|
||||
|
||||
79
src/exercise.rs
Normal file
79
src/exercise.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use serde::Deserialize;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::fs::remove_file;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{self, Command, Output};
|
||||
|
||||
const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"];
|
||||
|
||||
fn temp_file() -> String {
|
||||
format!("./temp_{}", process::id())
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Mode {
|
||||
Compile,
|
||||
Test,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ExerciseList {
|
||||
pub exercises: Vec<Exercise>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Exercise {
|
||||
pub path: PathBuf,
|
||||
pub mode: Mode,
|
||||
}
|
||||
|
||||
impl Exercise {
|
||||
pub fn compile(&self) -> Output {
|
||||
match self.mode {
|
||||
Mode::Compile => Command::new("rustc")
|
||||
.args(&[self.path.to_str().unwrap(), "-o", &temp_file()])
|
||||
.args(RUSTC_COLOR_ARGS)
|
||||
.output(),
|
||||
Mode::Test => Command::new("rustc")
|
||||
.args(&["--test", self.path.to_str().unwrap(), "-o", &temp_file()])
|
||||
.args(RUSTC_COLOR_ARGS)
|
||||
.output(),
|
||||
}
|
||||
.expect("Failed to run 'compile' command.")
|
||||
}
|
||||
|
||||
pub fn run(&self) -> Output {
|
||||
Command::new(&temp_file())
|
||||
.output()
|
||||
.expect("Failed to run 'run' command")
|
||||
}
|
||||
|
||||
pub fn clean(&self) {
|
||||
let _ignored = remove_file(&temp_file());
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Exercise {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.path.to_str().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
|
||||
#[test]
|
||||
fn test_clean() {
|
||||
File::create(&temp_file()).unwrap();
|
||||
let exercise = Exercise {
|
||||
path: PathBuf::from("example.rs"),
|
||||
mode: Mode::Test,
|
||||
};
|
||||
exercise.clean();
|
||||
assert!(!Path::new(&temp_file()).exists());
|
||||
}
|
||||
}
|
||||
90
src/main.rs
90
src/main.rs
@@ -1,18 +1,17 @@
|
||||
use crate::exercise::{Exercise, ExerciseList};
|
||||
use crate::run::run;
|
||||
use crate::verify::verify;
|
||||
use clap::{crate_version, App, Arg, SubCommand};
|
||||
use notify::DebouncedEvent;
|
||||
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
|
||||
use std::io::BufRead;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::sync::mpsc::channel;
|
||||
use std::time::Duration;
|
||||
use syntect::easy::HighlightFile;
|
||||
use syntect::highlighting::{Style, ThemeSet};
|
||||
use syntect::parsing::SyntaxSet;
|
||||
use syntect::util::as_24_bit_terminal_escaped;
|
||||
|
||||
mod exercise;
|
||||
mod run;
|
||||
mod util;
|
||||
mod verify;
|
||||
|
||||
fn main() {
|
||||
@@ -31,11 +30,8 @@ fn main() {
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
let ss = SyntaxSet::load_defaults_newlines();
|
||||
let ts = ThemeSet::load_defaults();
|
||||
|
||||
if None == matches.subcommand_name() {
|
||||
println!("");
|
||||
println!();
|
||||
println!(r#" welcome to... "#);
|
||||
println!(r#" _ _ _ "#);
|
||||
println!(r#" _ __ _ _ ___| |_| (_)_ __ __ _ ___ "#);
|
||||
@@ -43,50 +39,78 @@ fn main() {
|
||||
println!(r#" | | | |_| \__ \ |_| | | | | | (_| \__ \ "#);
|
||||
println!(r#" |_| \__,_|___/\__|_|_|_| |_|\__, |___/ "#);
|
||||
println!(r#" |___/ "#);
|
||||
println!("");
|
||||
println!();
|
||||
}
|
||||
|
||||
if let Some(matches) = matches.subcommand_matches("run") {
|
||||
run(matches.clone());
|
||||
if !Path::new("info.toml").exists() {
|
||||
println!(
|
||||
"{} must be run from the rustlings directory",
|
||||
std::env::current_exe().unwrap().to_str().unwrap()
|
||||
);
|
||||
println!("Try `cd rustlings/`!");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
if let Some(_) = matches.subcommand_matches("verify") {
|
||||
match verify() {
|
||||
Ok(_) => {}
|
||||
Err(_) => std::process::exit(1),
|
||||
}
|
||||
let toml_str = &fs::read_to_string("info.toml").unwrap();
|
||||
let exercises = toml::from_str::<ExerciseList>(toml_str).unwrap().exercises;
|
||||
|
||||
if let Some(ref matches) = matches.subcommand_matches("run") {
|
||||
let filename = matches.value_of("file").unwrap_or_else(|| {
|
||||
println!("Please supply a file name!");
|
||||
std::process::exit(1);
|
||||
});
|
||||
|
||||
let matching_exercise = |e: &&Exercise| {
|
||||
Path::new(filename)
|
||||
.canonicalize()
|
||||
.map(|p| p.ends_with(&e.path))
|
||||
.unwrap_or(false)
|
||||
};
|
||||
|
||||
let exercise = exercises.iter().find(matching_exercise).unwrap_or_else(|| {
|
||||
println!("No exercise found for your file name!");
|
||||
std::process::exit(1)
|
||||
});
|
||||
|
||||
run(&exercise).unwrap_or_else(|_| std::process::exit(1));
|
||||
}
|
||||
|
||||
if let Some(_) = matches.subcommand_matches("watch") {
|
||||
watch().unwrap();
|
||||
if matches.subcommand_matches("verify").is_some() {
|
||||
verify(&exercises).unwrap_or_else(|_| std::process::exit(1));
|
||||
}
|
||||
|
||||
if let None = matches.subcommand_name() {
|
||||
let mut highlighter =
|
||||
HighlightFile::new("default_out.md", &ss, &ts.themes["base16-eighties.dark"]).unwrap();
|
||||
for maybe_line in highlighter.reader.lines() {
|
||||
let line = maybe_line.unwrap();
|
||||
let regions: Vec<(Style, &str)> = highlighter.highlight_lines.highlight(&line, &ss);
|
||||
println!("{}", as_24_bit_terminal_escaped(®ions[..], true));
|
||||
}
|
||||
if matches.subcommand_matches("watch").is_some() {
|
||||
watch(&exercises).unwrap();
|
||||
}
|
||||
|
||||
if matches.subcommand_name().is_none() {
|
||||
let text = fs::read_to_string("default_out.txt").unwrap();
|
||||
println!("{}", text);
|
||||
}
|
||||
|
||||
println!("\x1b[0m");
|
||||
}
|
||||
|
||||
fn watch() -> notify::Result<()> {
|
||||
fn watch(exercises: &[Exercise]) -> notify::Result<()> {
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(2))?;
|
||||
watcher.watch("./exercises", RecursiveMode::Recursive)?;
|
||||
watcher.watch(Path::new("./exercises"), RecursiveMode::Recursive)?;
|
||||
|
||||
let _ignored = verify();
|
||||
let _ignored = verify(exercises.iter());
|
||||
|
||||
loop {
|
||||
match rx.recv() {
|
||||
Ok(event) => match event {
|
||||
DebouncedEvent::Chmod(_) | DebouncedEvent::Write(_) => {
|
||||
let _ignored = verify();
|
||||
DebouncedEvent::Create(b) | DebouncedEvent::Chmod(b) | DebouncedEvent::Write(b) => {
|
||||
if b.extension() == Some(OsStr::new("rs")) {
|
||||
println!("----------**********----------\n");
|
||||
let filepath = b.as_path().canonicalize().unwrap();
|
||||
let exercise = exercises
|
||||
.iter()
|
||||
.skip_while(|e| !filepath.ends_with(&e.path));
|
||||
let _ignored = verify(exercise);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
|
||||
83
src/run.rs
83
src/run.rs
@@ -1,55 +1,52 @@
|
||||
use crate::util::clean;
|
||||
use crate::exercise::{Exercise, Mode};
|
||||
use crate::verify::test;
|
||||
use console::{style, Emoji};
|
||||
use indicatif::ProgressBar;
|
||||
use std::process::Command;
|
||||
|
||||
pub fn run(matches: clap::ArgMatches) {
|
||||
if let Some(filename) = matches.value_of("file") {
|
||||
if matches.is_present("test") {
|
||||
match test(filename) {
|
||||
Ok(_) => (),
|
||||
Err(_) => (),
|
||||
}
|
||||
std::process::exit(0);
|
||||
}
|
||||
let bar = ProgressBar::new_spinner();
|
||||
bar.set_message(format!("Compiling {}...", filename).as_str());
|
||||
bar.enable_steady_tick(100);
|
||||
let compilecmd = Command::new("rustc")
|
||||
.args(&[filename, "-o", "temp", "--color", "always"])
|
||||
.output()
|
||||
.expect("fail");
|
||||
bar.set_message(format!("Running {}...", filename).as_str());
|
||||
if compilecmd.status.success() {
|
||||
let runcmd = Command::new("./temp").output().expect("fail");
|
||||
bar.finish_and_clear();
|
||||
pub fn run(exercise: &Exercise) -> Result<(), ()> {
|
||||
match exercise.mode {
|
||||
Mode::Test => test(exercise)?,
|
||||
Mode::Compile => compile_and_run(exercise)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
if runcmd.status.success() {
|
||||
println!("{}", String::from_utf8_lossy(&runcmd.stdout));
|
||||
let formatstr = format!("{} Successfully ran {}", Emoji("✅", "✓"), filename);
|
||||
println!("{}", style(formatstr).green());
|
||||
clean();
|
||||
} else {
|
||||
println!("{}", String::from_utf8_lossy(&runcmd.stdout));
|
||||
println!("{}", String::from_utf8_lossy(&runcmd.stderr));
|
||||
pub fn compile_and_run(exercise: &Exercise) -> Result<(), ()> {
|
||||
let progress_bar = ProgressBar::new_spinner();
|
||||
progress_bar.set_message(format!("Compiling {}...", exercise).as_str());
|
||||
progress_bar.enable_steady_tick(100);
|
||||
|
||||
let formatstr = format!("{} Ran {} with errors", Emoji("⚠️ ", "!"), filename);
|
||||
println!("{}", style(formatstr).red());
|
||||
clean();
|
||||
}
|
||||
let compilecmd = exercise.compile();
|
||||
progress_bar.set_message(format!("Running {}...", exercise).as_str());
|
||||
if compilecmd.status.success() {
|
||||
let runcmd = exercise.run();
|
||||
progress_bar.finish_and_clear();
|
||||
|
||||
if runcmd.status.success() {
|
||||
println!("{}", String::from_utf8_lossy(&runcmd.stdout));
|
||||
let formatstr = format!("{} Successfully ran {}", Emoji("✅", "✓"), exercise);
|
||||
println!("{}", style(formatstr).green());
|
||||
exercise.clean();
|
||||
Ok(())
|
||||
} else {
|
||||
bar.finish_and_clear();
|
||||
let formatstr = format!(
|
||||
"{} Compilation of {} failed! Compiler error message:\n",
|
||||
Emoji("⚠️ ", "!"),
|
||||
filename
|
||||
);
|
||||
println!("{}", String::from_utf8_lossy(&runcmd.stdout));
|
||||
println!("{}", String::from_utf8_lossy(&runcmd.stderr));
|
||||
|
||||
let formatstr = format!("{} Ran {} with errors", Emoji("⚠️ ", "!"), exercise);
|
||||
println!("{}", style(formatstr).red());
|
||||
println!("{}", String::from_utf8_lossy(&compilecmd.stderr));
|
||||
clean();
|
||||
exercise.clean();
|
||||
Err(())
|
||||
}
|
||||
} else {
|
||||
panic!("Please supply a filename!");
|
||||
progress_bar.finish_and_clear();
|
||||
let formatstr = format!(
|
||||
"{} Compilation of {} failed! Compiler error message:\n",
|
||||
Emoji("⚠️ ", "!"),
|
||||
exercise
|
||||
);
|
||||
println!("{}", style(formatstr).red());
|
||||
println!("{}", String::from_utf8_lossy(&compilecmd.stderr));
|
||||
exercise.clean();
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
use std::fs::remove_file;
|
||||
|
||||
pub fn clean() {
|
||||
let _ignored = remove_file("temp");
|
||||
}
|
||||
119
src/verify.rs
119
src/verify.rs
@@ -1,124 +1,83 @@
|
||||
use crate::util::clean;
|
||||
use crate::exercise::{Exercise, Mode};
|
||||
use console::{style, Emoji};
|
||||
use indicatif::ProgressBar;
|
||||
use std::process::Command;
|
||||
|
||||
pub fn verify() -> Result<(), ()> {
|
||||
compile_only("exercises/variables/variables1.rs")?;
|
||||
compile_only("exercises/variables/variables2.rs")?;
|
||||
compile_only("exercises/variables/variables3.rs")?;
|
||||
compile_only("exercises/variables/variables4.rs")?;
|
||||
test("exercises/if/if1.rs")?;
|
||||
compile_only("exercises/functions/functions1.rs")?;
|
||||
compile_only("exercises/functions/functions2.rs")?;
|
||||
compile_only("exercises/functions/functions3.rs")?;
|
||||
compile_only("exercises/functions/functions4.rs")?;
|
||||
compile_only("exercises/functions/functions5.rs")?;
|
||||
test("exercises/test1.rs")?;
|
||||
compile_only("exercises/primitive_types/primitive_types1.rs")?;
|
||||
compile_only("exercises/primitive_types/primitive_types2.rs")?;
|
||||
compile_only("exercises/primitive_types/primitive_types3.rs")?;
|
||||
compile_only("exercises/primitive_types/primitive_types4.rs")?;
|
||||
compile_only("exercises/primitive_types/primitive_types5.rs")?;
|
||||
compile_only("exercises/primitive_types/primitive_types6.rs")?;
|
||||
test("exercises/tests/tests1.rs")?;
|
||||
test("exercises/tests/tests2.rs")?;
|
||||
test("exercises/tests/tests3.rs")?;
|
||||
test("exercises/test2.rs")?;
|
||||
compile_only("exercises/strings/strings1.rs")?;
|
||||
compile_only("exercises/strings/strings2.rs")?;
|
||||
compile_only("exercises/test3.rs")?;
|
||||
compile_only("exercises/modules/modules1.rs")?;
|
||||
compile_only("exercises/modules/modules2.rs")?;
|
||||
compile_only("exercises/macros/macros1.rs")?;
|
||||
compile_only("exercises/macros/macros2.rs")?;
|
||||
compile_only("exercises/macros/macros3.rs")?;
|
||||
compile_only("exercises/macros/macros4.rs")?;
|
||||
compile_only("exercises/test4.rs")?;
|
||||
compile_only("exercises/move_semantics/move_semantics1.rs")?;
|
||||
compile_only("exercises/move_semantics/move_semantics2.rs")?;
|
||||
compile_only("exercises/move_semantics/move_semantics3.rs")?;
|
||||
compile_only("exercises/move_semantics/move_semantics4.rs")?;
|
||||
test("exercises/error_handling/errors1.rs")?;
|
||||
test("exercises/error_handling/errors2.rs")?;
|
||||
test("exercises/error_handling/errors3.rs")?;
|
||||
test("exercises/error_handling/errorsn.rs")?;
|
||||
compile_only("exercises/error_handling/option1.rs")?;
|
||||
test("exercises/error_handling/result1.rs")?;
|
||||
compile_only("exercises/threads/threads1.rs")?;
|
||||
pub fn verify<'a>(start_at: impl IntoIterator<Item = &'a Exercise>) -> Result<(), ()> {
|
||||
for exercise in start_at {
|
||||
match exercise.mode {
|
||||
Mode::Test => test(&exercise)?,
|
||||
Mode::Compile => compile_only(&exercise)?,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_only(filename: &str) -> Result<(), ()> {
|
||||
let bar = ProgressBar::new_spinner();
|
||||
bar.set_message(format!("Compiling {}...", filename).as_str());
|
||||
bar.enable_steady_tick(100);
|
||||
let compilecmd = Command::new("rustc")
|
||||
.args(&[filename, "-o", "temp", "--color", "always"])
|
||||
.output()
|
||||
.expect("fail");
|
||||
bar.finish_and_clear();
|
||||
if compilecmd.status.success() {
|
||||
fn compile_only(exercise: &Exercise) -> Result<(), ()> {
|
||||
let progress_bar = ProgressBar::new_spinner();
|
||||
progress_bar.set_message(format!("Compiling {}...", exercise).as_str());
|
||||
progress_bar.enable_steady_tick(100);
|
||||
let compile_output = exercise.compile();
|
||||
progress_bar.finish_and_clear();
|
||||
if compile_output.status.success() {
|
||||
let formatstr = format!(
|
||||
"{} Successfully compiled {}!",
|
||||
Emoji("✅", "✓"),
|
||||
filename
|
||||
exercise
|
||||
);
|
||||
println!("{}", style(formatstr).green());
|
||||
clean();
|
||||
exercise.clean();
|
||||
Ok(())
|
||||
} else {
|
||||
let formatstr = format!(
|
||||
"{} Compilation of {} failed! Compiler error message:\n",
|
||||
Emoji("⚠️ ", "!"),
|
||||
filename
|
||||
exercise
|
||||
);
|
||||
println!("{}", style(formatstr).red());
|
||||
println!("{}", String::from_utf8_lossy(&compilecmd.stderr));
|
||||
clean();
|
||||
println!("{}", String::from_utf8_lossy(&compile_output.stderr));
|
||||
exercise.clean();
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test(filename: &str) -> Result<(), ()> {
|
||||
let bar = ProgressBar::new_spinner();
|
||||
bar.set_message(format!("Testing {}...", filename).as_str());
|
||||
bar.enable_steady_tick(100);
|
||||
let testcmd = Command::new("rustc")
|
||||
.args(&["--test", filename, "-o", "temp", "--color", "always"])
|
||||
.output()
|
||||
.expect("fail");
|
||||
if testcmd.status.success() {
|
||||
bar.set_message(format!("Running {}...", filename).as_str());
|
||||
let runcmd = Command::new("./temp").output().expect("fail");
|
||||
bar.finish_and_clear();
|
||||
pub fn test(exercise: &Exercise) -> Result<(), ()> {
|
||||
let progress_bar = ProgressBar::new_spinner();
|
||||
progress_bar.set_message(format!("Testing {}...", exercise).as_str());
|
||||
progress_bar.enable_steady_tick(100);
|
||||
|
||||
let compile_output = exercise.compile();
|
||||
if compile_output.status.success() {
|
||||
progress_bar.set_message(format!("Running {}...", exercise).as_str());
|
||||
|
||||
let runcmd = exercise.run();
|
||||
progress_bar.finish_and_clear();
|
||||
|
||||
if runcmd.status.success() {
|
||||
let formatstr = format!("{} Successfully tested {}!", Emoji("✅", "✓"), filename);
|
||||
let formatstr = format!("{} Successfully tested {}!", Emoji("✅", "✓"), exercise);
|
||||
println!("{}", style(formatstr).green());
|
||||
clean();
|
||||
exercise.clean();
|
||||
Ok(())
|
||||
} else {
|
||||
let formatstr = format!(
|
||||
"{} Testing of {} failed! Please try again. Here's the output:",
|
||||
Emoji("⚠️ ", "!"),
|
||||
filename
|
||||
exercise
|
||||
);
|
||||
println!("{}", style(formatstr).red());
|
||||
println!("{}", String::from_utf8_lossy(&runcmd.stdout));
|
||||
clean();
|
||||
exercise.clean();
|
||||
Err(())
|
||||
}
|
||||
} else {
|
||||
bar.finish_and_clear();
|
||||
progress_bar.finish_and_clear();
|
||||
let formatstr = format!(
|
||||
"{} Compiling of {} failed! Please try again. Here's the output:",
|
||||
Emoji("⚠️ ", "!"),
|
||||
filename
|
||||
exercise
|
||||
);
|
||||
println!("{}", style(formatstr).red());
|
||||
println!("{}", String::from_utf8_lossy(&testcmd.stderr));
|
||||
clean();
|
||||
println!("{}", String::from_utf8_lossy(&compile_output.stderr));
|
||||
exercise.clean();
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
3
tests/fixture/failure/compFailure.rs
Normal file
3
tests/fixture/failure/compFailure.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
let
|
||||
}
|
||||
2
tests/fixture/failure/compNoExercise.rs
Normal file
2
tests/fixture/failure/compNoExercise.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
fn main() {
|
||||
}
|
||||
7
tests/fixture/failure/info.toml
Normal file
7
tests/fixture/failure/info.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[[exercises]]
|
||||
path = "compFailure.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "testFailure.rs"
|
||||
mode = "test"
|
||||
4
tests/fixture/failure/testFailure.rs
Normal file
4
tests/fixture/failure/testFailure.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
#[test]
|
||||
fn passing() {
|
||||
asset!(true);
|
||||
}
|
||||
4
tests/fixture/failure/testNotPassed.rs
Normal file
4
tests/fixture/failure/testNotPassed.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
#[test]
|
||||
fn not_passing() {
|
||||
assert!(false);
|
||||
}
|
||||
2
tests/fixture/success/compSuccess.rs
Normal file
2
tests/fixture/success/compSuccess.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
fn main() {
|
||||
}
|
||||
7
tests/fixture/success/info.toml
Normal file
7
tests/fixture/success/info.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[[exercises]]
|
||||
path = "compSuccess.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "testSuccess.rs"
|
||||
mode = "test"
|
||||
4
tests/fixture/success/testSuccess.rs
Normal file
4
tests/fixture/success/testSuccess.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
#[test]
|
||||
fn passing() {
|
||||
assert!(true);
|
||||
}
|
||||
107
tests/integration_tests.rs
Normal file
107
tests/integration_tests.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
use assert_cmd::prelude::*;
|
||||
use std::process::Command;
|
||||
|
||||
#[test]
|
||||
fn runs_without_arguments() {
|
||||
let mut cmd = Command::cargo_bin("rustlings").unwrap();
|
||||
cmd.assert().success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fails_when_in_wrong_dir() {
|
||||
Command::cargo_bin("rustlings")
|
||||
.unwrap()
|
||||
.current_dir("tests/")
|
||||
.assert()
|
||||
.code(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_all_success() {
|
||||
Command::cargo_bin("rustlings")
|
||||
.unwrap()
|
||||
.arg("v")
|
||||
.current_dir("tests/fixture/success")
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_all_failure() {
|
||||
Command::cargo_bin("rustlings")
|
||||
.unwrap()
|
||||
.arg("v")
|
||||
.current_dir("tests/fixture/failure")
|
||||
.assert()
|
||||
.code(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_single_compile_success() {
|
||||
Command::cargo_bin("rustlings")
|
||||
.unwrap()
|
||||
.args(&["r", "compSuccess.rs"])
|
||||
.current_dir("tests/fixture/success/")
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_single_compile_failure() {
|
||||
Command::cargo_bin("rustlings")
|
||||
.unwrap()
|
||||
.args(&["r", "compFailure.rs"])
|
||||
.current_dir("tests/fixture/failure/")
|
||||
.assert()
|
||||
.code(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_single_test_success() {
|
||||
Command::cargo_bin("rustlings")
|
||||
.unwrap()
|
||||
.args(&["r", "testSuccess.rs"])
|
||||
.current_dir("tests/fixture/success/")
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_single_test_failure() {
|
||||
Command::cargo_bin("rustlings")
|
||||
.unwrap()
|
||||
.args(&["r", "testFailure.rs"])
|
||||
.current_dir("tests/fixture/failure/")
|
||||
.assert()
|
||||
.code(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_single_test_not_passed() {
|
||||
Command::cargo_bin("rustlings")
|
||||
.unwrap()
|
||||
.args(&["r", "testNotPassed.rs"])
|
||||
.current_dir("tests/fixture/failure/")
|
||||
.assert()
|
||||
.code(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_single_test_no_filename() {
|
||||
Command::cargo_bin("rustlings")
|
||||
.unwrap()
|
||||
.arg("r")
|
||||
.current_dir("tests/fixture/")
|
||||
.assert()
|
||||
.code(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_single_test_no_exercise() {
|
||||
Command::cargo_bin("rustlings")
|
||||
.unwrap()
|
||||
.args(&["r", "compNoExercise.rs"])
|
||||
.current_dir("tests/fixture/failure")
|
||||
.assert()
|
||||
.code(1);
|
||||
}
|
||||
Reference in New Issue
Block a user