From e5efc68a9101d7d7e38263c8a6ee44dda991fc6a Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 28 Mar 2024 17:34:48 +0100 Subject: [PATCH] Done macro --- Cargo.lock | 8 ++++ Cargo.toml | 21 +++++--- rustlings-macros/Cargo.toml | 12 +++++ rustlings-macros/src/lib.rs | 95 +++++++++++++++++++++++++++++++++++++ src/main.rs | 23 +++++++++ 5 files changed, 152 insertions(+), 7 deletions(-) create mode 100644 rustlings-macros/Cargo.toml create mode 100644 rustlings-macros/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index f4853d0c..e432072d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -574,6 +574,7 @@ dependencies = [ "indicatif", "notify-debouncer-mini", "predicates", + "rustlings-macros", "serde", "serde_json", "shlex", @@ -582,6 +583,13 @@ dependencies = [ "winnow", ] +[[package]] +name = "rustlings-macros" +version = "5.6.1" +dependencies = [ + "quote", +] + [[package]] name = "ryu" version = "1.0.17" diff --git a/Cargo.toml b/Cargo.toml index 2d152cfc..e08be8bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,19 +1,30 @@ -[package] -name = "rustlings" -description = "Small exercises to get you used to reading and writing Rust code!" +[workspace] +resolver = "2" + +[workspace.package] version = "5.6.1" authors = [ "Liv ", "Carol (Nichols || Goulding) ", ] +license = "MIT" edition = "2021" +[package] +name = "rustlings" +description = "Small exercises to get you used to reading and writing Rust code!" +version.workspace = true +authors.workspace = true +license.workspace = true +edition.workspace = true + [dependencies] anyhow = "1.0.81" clap = { version = "4.5.4", features = ["derive"] } console = "0.15.8" indicatif = "0.17.8" notify-debouncer-mini = "0.4.1" +rustlings-macros = { path = "rustlings-macros" } serde_json = "1.0.115" serde = { version = "1.0.197", features = ["derive"] } shlex = "1.3.0" @@ -21,10 +32,6 @@ toml_edit = { version = "0.22.9", default-features = false, features = ["parse", which = "6.0.1" winnow = "0.6.5" -[[bin]] -name = "rustlings" -path = "src/main.rs" - [dev-dependencies] assert_cmd = "2.0.14" glob = "0.3.0" diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml new file mode 100644 index 00000000..0114c8f0 --- /dev/null +++ b/rustlings-macros/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "rustlings-macros" +version.workspace = true +authors.workspace = true +license.workspace = true +edition.workspace = true + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0.35" diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs new file mode 100644 index 00000000..dd1a5881 --- /dev/null +++ b/rustlings-macros/src/lib.rs @@ -0,0 +1,95 @@ +use proc_macro::TokenStream; +use quote::quote; +use std::{fs::read_dir, panic, path::PathBuf}; + +fn path_to_string(path: PathBuf) -> String { + path.into_os_string() + .into_string() + .unwrap_or_else(|original| { + panic!("The path {} is invalid UTF8", original.to_string_lossy()); + }) +} + +#[proc_macro] +pub fn include_files(_: TokenStream) -> TokenStream { + let mut files = Vec::with_capacity(8); + let mut dirs = Vec::with_capacity(128); + + for entry in read_dir("exercises").expect("Failed to open the exercises directory") { + let entry = entry.expect("Failed to read the exercises directory"); + + if entry.file_type().unwrap().is_file() { + let path = entry.path(); + if path.file_name().unwrap() != "README.md" { + files.push(path_to_string(path)); + } + + continue; + } + + let dir_path = entry.path(); + let dir_files = read_dir(&dir_path).unwrap_or_else(|e| { + panic!("Failed to open the directory {}: {e}", dir_path.display()); + }); + let dir_path = path_to_string(dir_path); + let dir_files = dir_files.filter_map(|entry| { + let entry = entry.unwrap_or_else(|e| { + panic!("Failed to read the directory {dir_path}: {e}"); + }); + let path = entry.path(); + + if !entry.file_type().unwrap().is_file() { + panic!("Found {} but expected only files", path.display()); + } + + if path.file_name().unwrap() == "README.md" { + return None; + } + + if path.extension() != Some("rs".as_ref()) { + panic!( + "Found {} but expected only README.md and .rs files", + path.display(), + ); + } + + Some(path_to_string(path)) + }); + + dirs.push(quote! { + EmbeddedFlatDir { + path: #dir_path, + readme: EmbeddedFile { + path: concat!(#dir_path, "/README.md"), + content: ::std::include_bytes!(concat!("../", #dir_path, "/README.md")), + }, + content: vec![ + #(EmbeddedFile { + path: #dir_files, + content: ::std::include_bytes!(concat!("../", #dir_files)), + }),* + ], + } + }); + } + + quote! { + EmbeddedFiles { + info_toml_content: ::std::include_str!("../info.toml"), + exercises_dir: ExercisesDir { + readme: EmbeddedFile { + path: "exercises/README.md", + content: ::std::include_bytes!("../exercises/README.md"), + }, + files: vec![#( + EmbeddedFile { + path: #files, + content: ::std::include_bytes!(concat!("../", #files)), + } + ),*], + dirs: vec![#(#dirs),*], + }, + } + } + .into() +} diff --git a/src/main.rs b/src/main.rs index 8f73dbba..fed8c117 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,6 +27,28 @@ mod project; mod run; mod verify; +struct EmbeddedFile { + path: &'static str, + content: &'static [u8], +} + +struct EmbeddedFlatDir { + path: &'static str, + readme: EmbeddedFile, + content: Vec, +} + +struct ExercisesDir { + readme: EmbeddedFile, + files: Vec, + dirs: Vec, +} + +struct EmbeddedFiles { + info_toml_content: &'static str, + exercises_dir: ExercisesDir, +} + /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code #[derive(Parser)] #[command(version)] @@ -87,6 +109,7 @@ enum Subcommands { } fn main() -> Result<()> { + let embedded_files = rustlings_macros::include_files!(); let args = Args::parse(); if args.command.is_none() {