1
0
mirror of https://github.com/rust-lang/rustlings.git synced 2025-12-26 00:11:49 +02:00

Compare commits

...

29 Commits
1.0.0 ... 1.1.0

Author SHA1 Message Date
lyn
f3ee70489f 1.1.0 2019-03-20 21:27:27 +01:00
lyn
6a27ba735c cargo fmt 2019-03-20 21:25:45 +01:00
komaeda
91dce31265 Merge pull request #131 from ColinPitrat/master
Verify that rust version is recent enough to install rustlings.
2019-03-20 21:22:04 +01:00
lyn
040ca18a64 add travis config 2019-03-20 21:08:08 +01:00
lyn
f43cb124f6 add tests 2019-03-20 21:05:45 +01:00
komaeda
11875aed6e adjust author name 2019-03-20 14:51:28 +01:00
Colin Pitrat
f07703eb7a Fix comment position 2019-03-20 11:21:15 +00:00
Colin Pitrat
fd4eda8bda Verify that rust version is recent enough to install rustlings.
I would have liked to write some tests for the vercomp function I
introduce, but there doesn't seem to be any CI setup yet?
2019-03-20 11:18:39 +00:00
komaeda
bf8d927ab2 Merge pull request #123 from kisom/master
Be nicer when rustlings isn't run from the right directory.
2019-03-17 22:28:03 +01:00
Kyle Isom
9fc4a83987 Be nicer when rustlings isn't run from the right directory.
Before, rustlings would panic if it wasn't in the right directory. It
took me a minute to figure out why, and this wasn't my first intro to
Rust. It would probably help new users if they saw a helpful message
instead of a stack trace.
2019-03-17 11:43:47 -07:00
komaeda
63280ed9e4 Merge pull request #119 from LesnyRumcajs/patch-1
Add standard library types to exercises suite
2019-03-17 13:27:48 +01:00
komaeda
25f9d61410 Merge pull request #124 from kisom/update-link
errors2.rs: update link to Rust book.
2019-03-17 13:19:57 +01:00
Kyle Isom
c1f4257a91 errors2.rs: update link to Rust book. 2019-03-16 19:22:06 -07:00
komaeda
8f9d7ce3d8 Merge pull request #120 from abagshaw/master
Start verification at most recently modified file
2019-03-16 12:54:09 +01:00
Andrew Bagshaw
3b5dfac44e Remove unnessecary whitespace 2019-03-15 16:01:45 -07:00
Andrew Bagshaw
a6a8b61b12 Change to \n 2019-03-15 12:47:06 -07:00
Andrew Bagshaw
6cd42bb821 Add clear break between verify executions 2019-03-13 14:08:28 -07:00
Andrew Bagshaw
4d7ce6e571 deduplicate 2019-03-13 13:53:24 -07:00
Andrew Bagshaw
3f114cc069 Start verification at most recently modified file 2019-03-13 13:50:54 -07:00
LesnyRumcajs
58ccd72aff Add standard library types to exercises suite 2019-03-13 15:29:02 +01:00
lyn
abf175111d clippy-ify 2019-03-11 15:09:20 +01:00
lyn
9144c816bf remove obsolete paragraph on --test flag 2019-03-06 22:01:45 +01:00
lyn
999601d828 1.0.1 2019-03-06 21:58:10 +01:00
lyn
10d4d61d19 rework contribution guide for the new toml file 2019-03-06 21:55:48 +01:00
lyn
70e59cca3c standardize exercise running via an external toml file 2019-03-06 21:47:33 +01:00
komaeda
7d6e2812fb Merge pull request #117 from shaunbennett/master
Watch for file creation events in watch mode
2019-03-06 20:28:29 +01:00
lyn
05e8f02d0a edit readme to use the install script 2019-03-06 20:25:27 +01:00
lyn
9a14d72f08 add a basic install script 2019-03-06 20:16:31 +01:00
Shaun Bennett
04d0f78a2c Fix file watching for vim swap files 2019-03-06 18:38:55 +00:00
15 changed files with 572 additions and 132 deletions

11
.travis.yml Normal file
View File

@@ -0,0 +1,11 @@
language: rust
rust:
- stable
- beta
- nightly
script: cargo test --verbose -- --test-threads=1
matrix:
allow_failures:
- rust: nightly
fast_finish: true
cache: cargo

View File

@@ -1,7 +1,7 @@
[package]
name = "rustlings"
version = "1.0.0"
authors = ["Lynn <819880950@qq.com>", "Carol (Nichols || Goulding) <carol.nichols@gmail.com"]
version = "1.1.0"
authors = ["Olivia <819880950@qq.com>", "Carol (Nichols || Goulding) <carol.nichols@gmail.com"]
edition = "2018"
[dependencies]
@@ -10,3 +10,11 @@ indicatif = "0.9.0"
console = "0.6.2"
syntect = "3.0.2"
notify = "4.0.0"
toml = "0.4.10"
[[bin]]
name = "rustlings"
path = "src/main.rs"
[dev-dependencies]
assert_cmd = "0.11.0"

View File

@@ -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

View File

@@ -68,5 +68,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!

213
info.toml Normal file
View File

@@ -0,0 +1,213 @@
# 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"
# 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
View File

@@ -0,0 +1,103 @@
#!/usr/bin/env bash
echo "Let's get you set up with Rustlings!"
echo "Checking requirements..."
if [ -x "$(git)" ]
then
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
else
echo "SUCCESS: Git is installed"
fi
if [ -x "$(rustc)" ]
then
echo "WARNING: Rust does not seem to be installed."
echo "Please download Rust using https://rustup.rs!"
exit 1
else
echo "SUCCESS: Rust is installed"
fi
if [ -x "$(cargo)" ]
then
echo "WARNING: Cargo does not seem to be installed."
echo "Please download Rust and Cargo using https://rustup.rs!"
exit 1
else
echo "SUCCESS: Cargo is installed"
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."

View File

@@ -3,7 +3,9 @@ use crate::verify::verify;
use clap::{crate_version, App, Arg, SubCommand};
use notify::DebouncedEvent;
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
use std::ffi::OsStr;
use std::io::BufRead;
use std::path::Path;
use std::sync::mpsc::channel;
use std::time::Duration;
use syntect::easy::HighlightFile;
@@ -35,7 +37,7 @@ fn main() {
let ts = ThemeSet::load_defaults();
if None == matches.subcommand_name() {
println!("");
println!();
println!(r#" welcome to... "#);
println!(r#" _ _ _ "#);
println!(r#" _ __ _ _ ___| |_| (_)_ __ __ _ ___ "#);
@@ -43,25 +45,33 @@ fn main() {
println!(r#" | | | |_| \__ \ |_| | | | | | (_| \__ \ "#);
println!(r#" |_| \__,_|___/\__|_|_|_| |_|\__, |___/ "#);
println!(r#" |___/ "#);
println!("");
println!();
}
if !Path::new("info.toml").exists() {
println!(
"{} must be run from the rustlings directory",
std::env::current_exe().unwrap().to_str().unwrap()
);
std::process::exit(1);
}
if let Some(matches) = matches.subcommand_matches("run") {
run(matches.clone());
run(matches.clone()).unwrap();
}
if let Some(_) = matches.subcommand_matches("verify") {
match verify() {
if matches.subcommand_matches("verify").is_some() {
match verify(None) {
Ok(_) => {}
Err(_) => std::process::exit(1),
}
}
if let Some(_) = matches.subcommand_matches("watch") {
if matches.subcommand_matches("watch").is_some() {
watch().unwrap();
}
if let None = matches.subcommand_name() {
if matches.subcommand_name().is_none() {
let mut highlighter =
HighlightFile::new("default_out.md", &ss, &ts.themes["base16-eighties.dark"]).unwrap();
for maybe_line in highlighter.reader.lines() {
@@ -80,13 +90,16 @@ fn watch() -> notify::Result<()> {
let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(2))?;
watcher.watch("./exercises", RecursiveMode::Recursive)?;
let _ignored = verify();
let _ignored = verify(None);
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 _ignored = verify(Some(b.as_path().to_str().unwrap()));
}
}
_ => {}
},

View File

@@ -2,54 +2,71 @@ use crate::util::clean;
use crate::verify::test;
use console::{style, Emoji};
use indicatif::ProgressBar;
use std::fs;
use std::process::Command;
use toml::Value;
pub fn run(matches: clap::ArgMatches) {
pub fn run(matches: clap::ArgMatches) -> Result<(), ()> {
if let Some(filename) = matches.value_of("file") {
if matches.is_present("test") {
match test(filename) {
Ok(_) => (),
Err(_) => (),
}
std::process::exit(0);
let toml: Value = fs::read_to_string("info.toml").unwrap().parse().unwrap();
let tomlvec: &Vec<Value> = toml.get("exercises").unwrap().as_array().unwrap();
let mut exercises = tomlvec.clone();
exercises.retain(|i| i.get("path").unwrap().as_str().unwrap() == filename);
if exercises.is_empty() {
println!("No exercise found for your filename!");
std::process::exit(1);
}
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();
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));
let formatstr = format!("{} Ran {} with errors", Emoji("⚠️ ", "!"), filename);
println!("{}", style(formatstr).red());
clean();
}
} else {
bar.finish_and_clear();
let formatstr = format!(
"{} Compilation of {} failed! Compiler error message:\n",
Emoji("⚠️ ", "!"),
filename
);
println!("{}", style(formatstr).red());
println!("{}", String::from_utf8_lossy(&compilecmd.stderr));
clean();
let exercise: &Value = &exercises[0];
match exercise.get("mode").unwrap().as_str().unwrap() {
"test" => test(exercise.get("path").unwrap().as_str().unwrap())?,
"compile" => compile_and_run(exercise.get("path").unwrap().as_str().unwrap())?,
_ => (),
}
Ok(())
} else {
panic!("Please supply a filename!");
}
}
pub fn compile_and_run(filename: &str) -> Result<(), ()> {
let progress_bar = ProgressBar::new_spinner();
progress_bar.set_message(format!("Compiling {}...", filename).as_str());
progress_bar.enable_steady_tick(100);
let compilecmd = Command::new("rustc")
.args(&[filename, "-o", "temp", "--color", "always"])
.output()
.expect("fail");
progress_bar.set_message(format!("Running {}...", filename).as_str());
if compilecmd.status.success() {
let runcmd = Command::new("./temp").output().expect("fail");
progress_bar.finish_and_clear();
if runcmd.status.success() {
println!("{}", String::from_utf8_lossy(&runcmd.stdout));
let formatstr = format!("{} Successfully ran {}", Emoji("", ""), filename);
println!("{}", style(formatstr).green());
clean();
Ok(())
} else {
println!("{}", String::from_utf8_lossy(&runcmd.stdout));
println!("{}", String::from_utf8_lossy(&runcmd.stderr));
let formatstr = format!("{} Ran {} with errors", Emoji("⚠️ ", "!"), filename);
println!("{}", style(formatstr).red());
clean();
Err(())
}
} else {
progress_bar.finish_and_clear();
let formatstr = format!(
"{} Compilation of {} failed! Compiler error message:\n",
Emoji("⚠️ ", "!"),
filename
);
println!("{}", style(formatstr).red());
println!("{}", String::from_utf8_lossy(&compilecmd.stderr));
clean();
Err(())
}
}

View File

@@ -3,3 +3,10 @@ use std::fs::remove_file;
pub fn clean() {
let _ignored = remove_file("temp");
}
#[test]
fn test_clean() {
std::fs::File::create("temp").unwrap();
clean();
assert!(!std::path::Path::new("temp").exists());
}

View File

@@ -1,63 +1,44 @@
use crate::util::clean;
use console::{style, Emoji};
use indicatif::ProgressBar;
use std::fs;
use std::process::Command;
use toml::Value;
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(start_at: Option<&str>) -> Result<(), ()> {
let toml: Value = fs::read_to_string("info.toml").unwrap().parse().unwrap();
let tomlvec: &Vec<Value> = toml.get("exercises").unwrap().as_array().unwrap();
let mut hit_start_at = false;
for i in tomlvec {
let path = i.get("path").unwrap().as_str().unwrap();
if let Some(start_at) = start_at {
if start_at.ends_with(path) {
hit_start_at = true;
} else if !hit_start_at {
continue;
}
}
match i.get("mode").unwrap().as_str().unwrap() {
"test" => test(path)?,
"compile" => compile_only(path)?,
_ => (),
}
}
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 progress_bar = ProgressBar::new_spinner();
progress_bar.set_message(format!("Compiling {}...", filename).as_str());
progress_bar.enable_steady_tick(100);
let compilecmd = Command::new("rustc")
.args(&[filename, "-o", "temp", "--color", "always"])
.output()
.expect("fail");
bar.finish_and_clear();
progress_bar.finish_and_clear();
if compilecmd.status.success() {
let formatstr = format!(
"{} Successfully compiled {}!",
@@ -81,17 +62,17 @@ fn compile_only(filename: &str) -> Result<(), ()> {
}
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 progress_bar = ProgressBar::new_spinner();
progress_bar.set_message(format!("Testing {}...", filename).as_str());
progress_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());
progress_bar.set_message(format!("Running {}...", filename).as_str());
let runcmd = Command::new("./temp").output().expect("fail");
bar.finish_and_clear();
progress_bar.finish_and_clear();
if runcmd.status.success() {
let formatstr = format!("{} Successfully tested {}!", Emoji("", ""), filename);
@@ -110,7 +91,7 @@ pub fn test(filename: &str) -> Result<(), ()> {
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("⚠️ ", "!"),

View File

@@ -0,0 +1,2 @@
fn main() {
}

View File

@@ -0,0 +1,2 @@
fn main() {
}

7
tests/fixture/info.toml Normal file
View File

@@ -0,0 +1,7 @@
[[exercises]]
path = "compSuccess.rs"
mode = "compile"
[[exercises]]
path = "testSuccess.rs"
mode = "test"

View File

@@ -0,0 +1,4 @@
#[test]
fn passing() {
assert!(true);
}

View File

@@ -0,0 +1,67 @@
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()
.failure();
}
#[test]
fn verify_all_success() {
Command::cargo_bin("rustlings")
.unwrap()
.arg("v")
.current_dir("tests/fixture/")
.assert()
.success();
}
#[test]
fn run_single_compile_success() {
Command::cargo_bin("rustlings")
.unwrap()
.args(&["r", "compSuccess.rs"])
.current_dir("tests/fixture/")
.assert()
.success();
}
#[test]
fn run_single_test_success() {
Command::cargo_bin("rustlings")
.unwrap()
.args(&["r", "testSuccess.rs"])
.current_dir("tests/fixture/")
.assert()
.success();
}
#[test]
fn run_single_test_no_filename() {
Command::cargo_bin("rustlings")
.unwrap()
.arg("r")
.current_dir("tests/fixture/")
.assert()
.failure();
}
#[test]
fn run_single_test_no_exercise() {
Command::cargo_bin("rustlings")
.unwrap()
.args(&["r", "compNoExercise.rs"])
.current_dir("tests/fixture/")
.assert()
.failure();
}