mirror of
https://github.com/rust-lang/rustlings.git
synced 2025-12-26 00:11:49 +02:00
Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
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
|
||||
12
Cargo.toml
12
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.2.0"
|
||||
authors = ["Olivia <819880950@qq.com>", "Carol (Nichols || Goulding) <carol.nichols@gmail.com"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
@@ -11,3 +11,11 @@ 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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
0
exercises/error_handling/errors1.rs
Executable file → Normal file
0
exercises/error_handling/errors1.rs
Executable file → Normal file
2
exercises/error_handling/errors2.rs
Executable file → Normal file
2
exercises/error_handling/errors2.rs
Executable file → Normal 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!
|
||||
|
||||
0
exercises/error_handling/errors3.rs
Executable file → Normal file
0
exercises/error_handling/errors3.rs
Executable file → Normal file
0
exercises/error_handling/errorsn.rs
Executable file → Normal file
0
exercises/error_handling/errorsn.rs
Executable file → Normal file
0
exercises/error_handling/option1.rs
Executable file → Normal file
0
exercises/error_handling/option1.rs
Executable file → Normal file
0
exercises/error_handling/result1.rs
Executable file → Normal file
0
exercises/error_handling/result1.rs
Executable file → Normal file
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
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
0
exercises/standard_library_types/arc1.rs
Executable file → Normal file
0
exercises/standard_library_types/arc1.rs
Executable file → Normal file
0
exercises/standard_library_types/iterator3.rs
Executable file → Normal file
0
exercises/standard_library_types/iterator3.rs
Executable file → Normal file
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/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
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
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"
|
||||
|
||||
68
install.sh
68
install.sh
@@ -3,31 +3,83 @@
|
||||
echo "Let's get you set up with Rustlings!"
|
||||
|
||||
echo "Checking requirements..."
|
||||
if [ -x "$(git)" ]
|
||||
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
|
||||
else
|
||||
echo "SUCCESS: Git is installed"
|
||||
fi
|
||||
|
||||
if [ -x "$(rustc)" ]
|
||||
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
|
||||
else
|
||||
echo "SUCCESS: Rust is installed"
|
||||
fi
|
||||
|
||||
if [ -x "$(cargo)" ]
|
||||
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: Cargo is installed"
|
||||
echo "SUCCESS: Rust is up to date"
|
||||
fi
|
||||
|
||||
Path=${1:-rustlings/}
|
||||
|
||||
80
src/exercise.rs
Normal file
80
src/exercise.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
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)
|
||||
.args(&["--", "--nocapture"])
|
||||
.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::path::Path;
|
||||
use std::fs::File;
|
||||
|
||||
#[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());
|
||||
}
|
||||
}
|
||||
67
src/main.rs
67
src/main.rs
@@ -1,10 +1,13 @@
|
||||
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::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::io::BufRead;
|
||||
use std::path::Path;
|
||||
use std::sync::mpsc::channel;
|
||||
use std::time::Duration;
|
||||
use syntect::easy::HighlightFile;
|
||||
@@ -12,8 +15,8 @@ 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() {
|
||||
@@ -36,7 +39,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 +47,50 @@ fn main() {
|
||||
println!(r#" | | | |_| \__ \ |_| | | | | | (_| \__ \ "#);
|
||||
println!(r#" |_| \__,_|___/\__|_|_|_| |_|\__, |___/ "#);
|
||||
println!(r#" |___/ "#);
|
||||
println!("");
|
||||
println!();
|
||||
}
|
||||
|
||||
if let Some(matches) = matches.subcommand_matches("run") {
|
||||
run(matches.clone()).unwrap();
|
||||
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.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() {
|
||||
if matches.subcommand_matches("watch").is_some() {
|
||||
watch(&exercises).unwrap();
|
||||
}
|
||||
|
||||
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() {
|
||||
@@ -75,20 +103,25 @@ fn main() {
|
||||
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::Create(b) | DebouncedEvent::Chmod(b) | DebouncedEvent::Write(b) => {
|
||||
if b.extension() == Some(OsStr::new("rs")) {
|
||||
let _ignored = verify();
|
||||
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);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
||||
64
src/run.rs
64
src/run.rs
@@ -1,72 +1,52 @@
|
||||
use crate::util::clean;
|
||||
use crate::exercise::{Mode, Exercise};
|
||||
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) -> Result<(), ()> {
|
||||
if let Some(filename) = matches.value_of("file") {
|
||||
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 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 run(exercise: &Exercise) -> Result<(), ()> {
|
||||
match exercise.mode {
|
||||
Mode::Test => test(exercise)?,
|
||||
Mode::Compile => compile_and_run(exercise)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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 compilecmd = Command::new("rustc")
|
||||
.args(&[filename, "-o", "temp", "--color", "always"])
|
||||
.output()
|
||||
.expect("fail");
|
||||
bar.set_message(format!("Running {}...", filename).as_str());
|
||||
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 compilecmd = exercise.compile();
|
||||
progress_bar.set_message(format!("Running {}...", exercise).as_str());
|
||||
if compilecmd.status.success() {
|
||||
let runcmd = Command::new("./temp").output().expect("fail");
|
||||
bar.finish_and_clear();
|
||||
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("✅", "✓"), filename);
|
||||
let formatstr = format!("{} Successfully ran {}", Emoji("✅", "✓"), exercise);
|
||||
println!("{}", style(formatstr).green());
|
||||
clean();
|
||||
exercise.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);
|
||||
let formatstr = format!("{} Ran {} with errors", Emoji("⚠️ ", "!"), exercise);
|
||||
println!("{}", style(formatstr).red());
|
||||
clean();
|
||||
exercise.clean();
|
||||
Err(())
|
||||
}
|
||||
} else {
|
||||
bar.finish_and_clear();
|
||||
progress_bar.finish_and_clear();
|
||||
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();
|
||||
exercise.clean();
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
use std::fs::remove_file;
|
||||
|
||||
pub fn clean() {
|
||||
let _ignored = remove_file("temp");
|
||||
}
|
||||
@@ -1,93 +1,83 @@
|
||||
use crate::util::clean;
|
||||
use crate::exercise::{Exercise, Mode};
|
||||
use console::{style, Emoji};
|
||||
use indicatif::ProgressBar;
|
||||
use std::fs;
|
||||
use std::process::Command;
|
||||
use toml::Value;
|
||||
|
||||
pub fn verify() -> Result<(), ()> {
|
||||
let toml: Value = fs::read_to_string("info.toml").unwrap().parse().unwrap();
|
||||
let tomlvec: &Vec<Value> = toml.get("exercises").unwrap().as_array().unwrap();
|
||||
for i in tomlvec {
|
||||
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())?,
|
||||
_ => (),
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
97
tests/integration_tests.rs
Normal file
97
tests/integration_tests.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
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_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