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

Compare commits

..

22 Commits
1.0.1 ... 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
15 changed files with 226 additions and 37 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.1"
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]
@@ -11,3 +11,10 @@ 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

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

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!

View File

@@ -196,4 +196,18 @@ mode = "test"
[[exercises]]
path = "exercises/threads/threads1.rs"
mode = "compile"
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"

View File

@@ -30,6 +30,58 @@ 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

View File

@@ -5,6 +5,7 @@ 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;
@@ -36,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#" _ __ _ _ ___| |_| (_)_ __ __ _ ___ "#);
@@ -44,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()).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() {
@@ -81,14 +90,15 @@ 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::Create(b) | DebouncedEvent::Chmod(b) | DebouncedEvent::Write(b) => {
if b.extension() == Some(OsStr::new("rs")) {
let _ignored = verify();
println!("----------**********----------\n");
let _ignored = verify(Some(b.as_path().to_str().unwrap()));
}
}
_ => {}

View File

@@ -30,17 +30,17 @@ pub fn run(matches: clap::ArgMatches) -> Result<(), ()> {
}
pub fn compile_and_run(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.set_message(format!("Running {}...", filename).as_str());
progress_bar.set_message(format!("Running {}...", filename).as_str());
if compilecmd.status.success() {
let runcmd = Command::new("./temp").output().expect("fail");
bar.finish_and_clear();
progress_bar.finish_and_clear();
if runcmd.status.success() {
println!("{}", String::from_utf8_lossy(&runcmd.stdout));
@@ -58,7 +58,7 @@ pub fn compile_and_run(filename: &str) -> Result<(), ()> {
Err(())
}
} else {
bar.finish_and_clear();
progress_bar.finish_and_clear();
let formatstr = format!(
"{} Compilation of {} failed! Compiler error message:\n",
Emoji("⚠️ ", "!"),

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

@@ -5,13 +5,25 @@ use std::fs;
use std::process::Command;
use toml::Value;
pub fn verify() -> Result<(), ()> {
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(i.get("path").unwrap().as_str().unwrap())?,
"compile" => compile_only(i.get("path").unwrap().as_str().unwrap())?,
"test" => test(path)?,
"compile" => compile_only(path)?,
_ => (),
}
}
@@ -19,14 +31,14 @@ pub fn verify() -> Result<(), ()> {
}
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 {}!",
@@ -50,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);
@@ -79,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();
}