diff options
| author | komaeda <819880950@qq.com> | 2019-01-23 22:04:01 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-01-23 22:04:01 +0100 |
| commit | df3389cfb04d575b7fdee92e0dac42efd901a025 (patch) | |
| tree | 1e6c3681a2c65933d52bfb7f0e2abaf734d1e403 /src | |
| parent | 5a671079d6c4528fdab30937912099d45c029b49 (diff) | |
| parent | 92072f39ac6b334c27f08de4b5c408b0e98784e2 (diff) | |
Merge pull request #106 from rustlings/new
rustlings v2
Diffstat (limited to 'src')
| -rw-r--r-- | src/bin/generate_readme.rs | 49 | ||||
| -rw-r--r-- | src/main.rs | 96 | ||||
| -rw-r--r-- | src/run.rs | 55 | ||||
| -rw-r--r-- | src/util.rs | 5 | ||||
| -rw-r--r-- | src/verify.rs | 106 |
5 files changed, 262 insertions, 49 deletions
diff --git a/src/bin/generate_readme.rs b/src/bin/generate_readme.rs deleted file mode 100644 index a502fea..0000000 --- a/src/bin/generate_readme.rs +++ /dev/null @@ -1,49 +0,0 @@ -// This script reads README-template.md and generates the playground links -// from the Rust source files in the various directories. - -// To add a new exercise, add it to the appropriate place in README-template.md -// and then make sure to recompile this script (because the template gets -// included at compile time and then run it to generate a new version of -// README.md. - -extern crate handlebars; -extern crate prlink; -#[macro_use] -extern crate serde_json; - -use handlebars::{Handlebars, Helper, RenderContext, RenderError}; - -use std::fs::File; -use std::io::prelude::*; -use std::path::PathBuf; - -fn main() { - let mut template_file = File::open("README-template.hbs").unwrap(); - let mut template = String::new(); - template_file.read_to_string(&mut template).unwrap(); - - let autogenerated_notice = "This file was autogenerated by the script in src/bin/generate_readme.rs. -Please edit either the script or the template in README-template.md in -order to make changes here rather than committing the changes directly."; - - let mut generated_readme = File::create("README.md").unwrap(); - - let mut hbs = Handlebars::new(); - hbs.register_helper("playground_link", Box::new(playground_link_helper)); - - write!( - generated_readme, - "{}", - hbs.render_template( - &template, - &json!({ "autogenerated_notice": autogenerated_notice }), - ).unwrap() - ).unwrap(); -} - -fn playground_link_helper(h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> { - let filename = PathBuf::from(h.param(0).unwrap().value().as_str().unwrap()); - let link = prlink::linkify_file(&filename); - rc.writer.write(link.into_bytes().as_ref())?; - Ok(()) -} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..8f9ccea --- /dev/null +++ b/src/main.rs @@ -0,0 +1,96 @@ +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::io::BufRead; +use std::sync::mpsc::channel; +use std::time::Duration; +use syntect::easy::HighlightFile; +use syntect::highlighting::{Style, ThemeSet}; +use syntect::parsing::SyntaxSet; +use syntect::util::as_24_bit_terminal_escaped; + +mod run; +mod util; +mod verify; + +fn main() { + let matches = App::new("rustlings") + .version(crate_version!()) + .author("Olivia Hugger, Carol Nichols") + .about("Rustlings is a collection of small exercises to get you used to writing and reading Rust code") + .subcommand(SubCommand::with_name("verify").alias("v").about("Verifies all exercises according to the recommended order")) + .subcommand(SubCommand::with_name("watch").alias("w").about("Reruns `verify` when files were edited")) + .subcommand( + SubCommand::with_name("run") + .alias("r") + .about("Runs/Tests a single exercise") + .arg(Arg::with_name("file").required(true).index(1)) + .arg(Arg::with_name("test").short("t").long("test").help("Run the file as a test")), + ) + .get_matches(); + + let ss = SyntaxSet::load_defaults_newlines(); + let ts = ThemeSet::load_defaults(); + + if None == matches.subcommand_name() { + println!(""); + println!(r#" welcome to... "#); + println!(r#" _ _ _ "#); + println!(r#" _ __ _ _ ___| |_| (_)_ __ __ _ ___ "#); + println!(r#" | '__| | | / __| __| | | '_ \ / _` / __| "#); + println!(r#" | | | |_| \__ \ |_| | | | | | (_| \__ \ "#); + println!(r#" |_| \__,_|___/\__|_|_|_| |_|\__, |___/ "#); + println!(r#" |___/ "#); + println!(""); + } + + if let Some(matches) = matches.subcommand_matches("run") { + run(matches.clone()); + } + + if let Some(_) = matches.subcommand_matches("verify") { + match verify() { + Ok(_) => {} + Err(_) => std::process::exit(1), + } + } + + if let Some(_) = matches.subcommand_matches("watch") { + watch().unwrap(); + } + + if let None = matches.subcommand_name() { + let mut highlighter = + HighlightFile::new("default_out.md", &ss, &ts.themes["base16-eighties.dark"]).unwrap(); + for maybe_line in highlighter.reader.lines() { + let line = maybe_line.unwrap(); + let regions: Vec<(Style, &str)> = highlighter.highlight_lines.highlight(&line, &ss); + println!("{}", as_24_bit_terminal_escaped(®ions[..], true)); + } + } + + println!("\x1b[0m"); +} + +fn watch() -> notify::Result<()> { + let (tx, rx) = channel(); + + let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(2))?; + watcher.watch("./exercises", RecursiveMode::Recursive)?; + + let _ignored = verify(); + + loop { + match rx.recv() { + Ok(event) => match event { + DebouncedEvent::Chmod(_) | DebouncedEvent::Write(_) => { + let _ignored = verify(); + } + _ => {} + }, + Err(e) => println!("watch error: {:?}", e), + } + } +} diff --git a/src/run.rs b/src/run.rs new file mode 100644 index 0000000..809b79d --- /dev/null +++ b/src/run.rs @@ -0,0 +1,55 @@ +use crate::util::clean; +use crate::verify::test; +use console::{style, Emoji}; +use indicatif::ProgressBar; +use std::process::Command; + +pub fn run(matches: clap::ArgMatches) { + if let Some(filename) = matches.value_of("file") { + if matches.is_present("test") { + match test(filename) { + Ok(_) => (), + Err(_) => (), + } + std::process::exit(0); + } + 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"]) + .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(); + } + } else { + panic!("Please supply a filename!"); + } +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..37a2028 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,5 @@ +use std::fs::remove_file; + +pub fn clean() { + let _ignored = remove_file("temp"); +} diff --git a/src/verify.rs b/src/verify.rs new file mode 100644 index 0000000..9aa3a4b --- /dev/null +++ b/src/verify.rs @@ -0,0 +1,106 @@ +use crate::util::clean; +use console::{style, Emoji}; +use indicatif::ProgressBar; +use std::process::Command; + +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")?; + compile_only("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")?; + 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() { + let formatstr = format!( + "{} Successfully compiled {}!", + Emoji("✅", "✓"), + filename + ); + println!("{}", style(formatstr).green()); + clean(); + Ok(()) + } else { + 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(()) + } +} + +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"]) + .output() + .expect("fail"); + bar.finish_and_clear(); + if testcmd.status.success() { + let formatstr = format!("{} Successfully tested {}!", Emoji("✅", "✓"), filename); + println!("{}", style(formatstr).green()); + clean(); + Ok(()) + } else { + let formatstr = format!( + "{} Testing of {} failed! Please try again.", + Emoji("⚠️ ", "!"), + filename + ); + println!("{}", style(formatstr).red()); + clean(); + Err(()) + } +} |
