mirror of
https://github.com/rust-lang/rustlings.git
synced 2024-12-27 00:00:03 +03:00
Done macro
This commit is contained in:
parent
9691c3cb55
commit
e5efc68a91
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -574,6 +574,7 @@ dependencies = [
|
||||||
"indicatif",
|
"indicatif",
|
||||||
"notify-debouncer-mini",
|
"notify-debouncer-mini",
|
||||||
"predicates",
|
"predicates",
|
||||||
|
"rustlings-macros",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"shlex",
|
"shlex",
|
||||||
|
@ -582,6 +583,13 @@ dependencies = [
|
||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustlings-macros"
|
||||||
|
version = "5.6.1"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.17"
|
version = "1.0.17"
|
||||||
|
|
21
Cargo.toml
21
Cargo.toml
|
@ -1,19 +1,30 @@
|
||||||
[package]
|
[workspace]
|
||||||
name = "rustlings"
|
resolver = "2"
|
||||||
description = "Small exercises to get you used to reading and writing Rust code!"
|
|
||||||
|
[workspace.package]
|
||||||
version = "5.6.1"
|
version = "5.6.1"
|
||||||
authors = [
|
authors = [
|
||||||
"Liv <mokou@fastmail.com>",
|
"Liv <mokou@fastmail.com>",
|
||||||
"Carol (Nichols || Goulding) <carol.nichols@gmail.com>",
|
"Carol (Nichols || Goulding) <carol.nichols@gmail.com>",
|
||||||
]
|
]
|
||||||
|
license = "MIT"
|
||||||
edition = "2021"
|
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]
|
[dependencies]
|
||||||
anyhow = "1.0.81"
|
anyhow = "1.0.81"
|
||||||
clap = { version = "4.5.4", features = ["derive"] }
|
clap = { version = "4.5.4", features = ["derive"] }
|
||||||
console = "0.15.8"
|
console = "0.15.8"
|
||||||
indicatif = "0.17.8"
|
indicatif = "0.17.8"
|
||||||
notify-debouncer-mini = "0.4.1"
|
notify-debouncer-mini = "0.4.1"
|
||||||
|
rustlings-macros = { path = "rustlings-macros" }
|
||||||
serde_json = "1.0.115"
|
serde_json = "1.0.115"
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
shlex = "1.3.0"
|
shlex = "1.3.0"
|
||||||
|
@ -21,10 +32,6 @@ toml_edit = { version = "0.22.9", default-features = false, features = ["parse",
|
||||||
which = "6.0.1"
|
which = "6.0.1"
|
||||||
winnow = "0.6.5"
|
winnow = "0.6.5"
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "rustlings"
|
|
||||||
path = "src/main.rs"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assert_cmd = "2.0.14"
|
assert_cmd = "2.0.14"
|
||||||
glob = "0.3.0"
|
glob = "0.3.0"
|
||||||
|
|
12
rustlings-macros/Cargo.toml
Normal file
12
rustlings-macros/Cargo.toml
Normal 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"
|
95
rustlings-macros/src/lib.rs
Normal file
95
rustlings-macros/src/lib.rs
Normal 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()
|
||||||
|
}
|
23
src/main.rs
23
src/main.rs
|
@ -27,6 +27,28 @@ mod project;
|
||||||
mod run;
|
mod run;
|
||||||
mod verify;
|
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
|
/// Rustlings is a collection of small exercises to get you used to writing and reading Rust code
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(version)]
|
#[command(version)]
|
||||||
|
@ -87,6 +109,7 @@ enum Subcommands {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
|
let embedded_files = rustlings_macros::include_files!();
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
|
||||||
if args.command.is_none() {
|
if args.command.is_none() {
|
||||||
|
|
Loading…
Reference in a new issue