mirror of
https://github.com/rust-lang/rustlings.git
synced 2025-12-26 00:11:49 +02:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3ee70489f | ||
|
|
6a27ba735c | ||
|
|
91dce31265 | ||
|
|
040ca18a64 | ||
|
|
f43cb124f6 | ||
|
|
11875aed6e | ||
|
|
f07703eb7a | ||
|
|
fd4eda8bda | ||
|
|
bf8d927ab2 | ||
|
|
9fc4a83987 | ||
|
|
63280ed9e4 | ||
|
|
25f9d61410 | ||
|
|
c1f4257a91 | ||
|
|
8f9d7ce3d8 | ||
|
|
3b5dfac44e | ||
|
|
a6a8b61b12 | ||
|
|
6cd42bb821 | ||
|
|
4d7ce6e571 | ||
|
|
3f114cc069 | ||
|
|
58ccd72aff | ||
|
|
abf175111d | ||
|
|
9144c816bf |
11
.travis.yml
Normal file
11
.travis.yml
Normal 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
|
||||
11
Cargo.toml
11
Cargo.toml
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!
|
||||
|
||||
16
info.toml
16
info.toml
@@ -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"
|
||||
|
||||
52
install.sh
52
install.sh
@@ -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
|
||||
|
||||
26
src/main.rs
26
src/main.rs
@@ -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()));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
||||
12
src/run.rs
12
src/run.rs
@@ -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("⚠️ ", "!"),
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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("⚠️ ", "!"),
|
||||
|
||||
2
tests/fixture/compNoExercise.rs
Normal file
2
tests/fixture/compNoExercise.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
fn main() {
|
||||
}
|
||||
2
tests/fixture/compSuccess.rs
Normal file
2
tests/fixture/compSuccess.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
fn main() {
|
||||
}
|
||||
7
tests/fixture/info.toml
Normal file
7
tests/fixture/info.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[[exercises]]
|
||||
path = "compSuccess.rs"
|
||||
mode = "compile"
|
||||
|
||||
[[exercises]]
|
||||
path = "testSuccess.rs"
|
||||
mode = "test"
|
||||
4
tests/fixture/testSuccess.rs
Normal file
4
tests/fixture/testSuccess.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
#[test]
|
||||
fn passing() {
|
||||
assert!(true);
|
||||
}
|
||||
67
tests/integration_tests.rs
Normal file
67
tests/integration_tests.rs
Normal 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();
|
||||
}
|
||||
Reference in New Issue
Block a user