1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
use itertools::Itertools; #[macro_use] extern crate lazy_static; use tera::{Context, Tera}; use std::fmt; use std::path::PathBuf; use std::fs::create_dir_all; use failure::Fail; lazy_static! { pub static ref TEMPLATES: Tera = { match Tera::new("src/templates/**/*.tpl") { Ok(t) => t, Err(e) => { println!("Parsing error(s): {}", e); std::process::exit(1); }, } }; } #[derive(Debug, Fail)] pub enum GenerationError { RootDoesNotExistError(PathBuf), ExerciseExists(PathBuf) } impl fmt::Display for GenerationError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let msg = match self { GenerationError::RootDoesNotExistError(v) => format!("Path provided ({}) doesn\'t exist", v.to_str().unwrap()), GenerationError::ExerciseExists(v) => format!("Exercise {} already exists", v.display()), }; write!(f, "{}", msg) } } #[derive(Debug, Fail)] pub struct TestingError { } impl fmt::Display for TestingError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Failed to run test suite") } } #[derive(Debug)] pub struct Exercise { chapter: String, exercise: String, path: PathBuf, } impl fmt::Display for Exercise { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "chapter {} exercise {}", self.chapter, self.exercise) } } impl Exercise { fn new(root: &PathBuf, number: &str) -> Exercise { let (chapter, exercise_number) = number.split('-').collect_tuple().unwrap(); let path = root.join(format!("chapter-{}", chapter)).join(format!("exercise-{}", exercise_number)); Exercise { chapter: String::from(chapter), exercise: String::from(exercise_number), path } } fn generate(&self) -> Result<(), GenerationError> { if self.path.exists() { return Err(GenerationError::ExerciseExists((*self.path).to_path_buf())); } if let Some(v) = self.path.to_str() { create_dir_all(v).unwrap(); } self.generate_readme().unwrap(); self.generate_test().unwrap(); Ok(()) } fn generate_readme(&self) -> std::io::Result<()> { let mut context = Context::new(); context.insert("chapter", &self.chapter); context.insert("exercise", &self.exercise); let content = TEMPLATES.render("readme.md.tpl", &context).unwrap(); let filename = self.path.join("README.md"); std::fs::write(filename, content) } fn generate_test(&self) -> std::io::Result<()> { let context = Context::new(); let content = TEMPLATES.render("test.rkt.tpl", &context).unwrap(); let filename = self.path.join("test.rkt"); std::fs::write(filename, content) } } pub fn generate(root: &PathBuf, number: &str) -> Result<(), GenerationError> { if !root.exists() { return Err(GenerationError::RootDoesNotExistError(root.to_owned())); } Exercise::new(root, number).generate() } pub fn test(_root: &PathBuf, _number: &str) -> Result<(), TestingError> { Ok(()) } #[test] fn test_generate_exercise() -> Result<(), std::io::Error> { use tempfile::tempdir; let root = tempdir()?.into_path(); generate(&root, "1-1").unwrap(); let exercise_root = root.join("chapter-1").join("exercise-1"); assert_eq!(exercise_root.exists(), true); assert_eq!(exercise_root.join("README.md").exists(), true); assert_eq!(exercise_root.join("test.rkt").exists(), true); Ok(()) }