Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Canonicalize paths to fix path matching #143

Merged
merged 3 commits into from
Apr 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ 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"
Expand Down
79 changes: 79 additions & 0 deletions src/exercise.rs
Original file line number Diff line number Diff line change
@@ -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::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());
}
}
42 changes: 34 additions & 8 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
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;
Expand All @@ -13,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() {
Expand Down Expand Up @@ -56,16 +58,36 @@ fn main() {
std::process::exit(1);
}

if let Some(matches) = matches.subcommand_matches("run") {
run(matches.clone()).unwrap_or_else(|_| 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 matches.subcommand_matches("verify").is_some() {
verify(None).unwrap_or_else(|_| std::process::exit(1));
verify(&exercises).unwrap_or_else(|_| std::process::exit(1));
}

if matches.subcommand_matches("watch").is_some() {
watch().unwrap();
watch(&exercises).unwrap();
}

if matches.subcommand_name().is_none() {
Expand All @@ -81,21 +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(Path::new("./exercises"), RecursiveMode::Recursive)?;

let _ignored = verify(None);
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")) {
println!("----------**********----------\n");
let _ignored = verify(Some(b.as_path().to_str().unwrap()));
let filepath = b.as_path().canonicalize().unwrap();
let exercise = exercises
.iter()
.skip_while(|e| !filepath.ends_with(&e.path));
let _ignored = verify(exercise);
}
}
_ => {}
Expand Down
51 changes: 17 additions & 34 deletions src/run.rs
Original file line number Diff line number Diff line change
@@ -1,69 +1,52 @@
use crate::util;
use crate::exercise::{Mode, Exercise};
use crate::verify::test;
use console::{style, Emoji};
use indicatif::ProgressBar;
use std::fs;
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<(), ()> {
pub fn compile_and_run(exercise: &Exercise) -> Result<(), ()> {
let progress_bar = ProgressBar::new_spinner();
progress_bar.set_message(format!("Compiling {}...", filename).as_str());
progress_bar.set_message(format!("Compiling {}...", exercise).as_str());
progress_bar.enable_steady_tick(100);

let compilecmd = util::compile_cmd(filename);
progress_bar.set_message(format!("Running {}...", filename).as_str());
let compilecmd = exercise.compile();
progress_bar.set_message(format!("Running {}...", exercise).as_str());
if compilecmd.status.success() {
let runcmd = util::run_cmd();
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());
util::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());
util::clean();
exercise.clean();
Err(())
}
} else {
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));
util::clean();
exercise.clean();
Err(())
}
}
41 changes: 0 additions & 41 deletions src/util.rs

This file was deleted.

Loading