Done macro

This commit is contained in:
mo8it 2024-03-28 17:34:48 +01:00
parent 9691c3cb55
commit e5efc68a91
5 changed files with 152 additions and 7 deletions

8
Cargo.lock generated
View file

@ -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"

View file

@ -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 <mokou@fastmail.com>",
"Carol (Nichols || Goulding) <carol.nichols@gmail.com>",
]
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"

View file

@ -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"

View file

@ -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()
}

View file

@ -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<EmbeddedFile>,
}
struct ExercisesDir {
readme: EmbeddedFile,
files: Vec<EmbeddedFile>,
dirs: Vec<EmbeddedFlatDir>,
}
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() {