mirror of
https://github.com/rust-lang/rustlings.git
synced 2026-11-12 23:45:57 +00:00
replace rustlings-macros with build script
This turns rustlings into a single crate, which is less complicated to publish releases for. A follow-up step might be to remove the workspace configuration. The motivation was this discussion on Zulip: https://rust-lang.zulipchat.com/#narrow/channel/334454-t-lang-docs.2Frustlings/topic/Trusted.20publishing/near/638396712
This commit is contained in:
+1
-1
@@ -52,7 +52,7 @@ When updating an exercise, check if its solution needs to be updated.
|
||||
- Make sure to put in some helpful links, and link to sections of The Book in `exercises/yourTopic/README.md`.
|
||||
- In the exercise, add a `// TODO: …` comment where user changes are required.
|
||||
- Add a solution at `solutions/yourTopic/yourTopicN.rs` with comments explaining it.
|
||||
- Add the [metadata for your exercise](#exercise-metadata) in the `rustlings-macros/info.toml` file.
|
||||
- Add the [metadata for your exercise](#exercise-metadata) in the `info.toml` file.
|
||||
- [Open a pull request](#pull-requests).
|
||||
|
||||
### Exercise Metadata
|
||||
|
||||
Generated
+1
-10
@@ -434,8 +434,8 @@ dependencies = [
|
||||
"clap",
|
||||
"crossterm",
|
||||
"notify",
|
||||
"quote",
|
||||
"rustix",
|
||||
"rustlings-macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shlex",
|
||||
@@ -444,15 +444,6 @@ dependencies = [
|
||||
"wait-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustlings-macros"
|
||||
version = "6.5.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"serde",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
|
||||
+7
-1
@@ -36,10 +36,12 @@ keywords = [
|
||||
]
|
||||
include = [
|
||||
"/src/",
|
||||
"/build.rs",
|
||||
"/exercises/",
|
||||
"/solutions/",
|
||||
# A symlink to be able to include `dev/Cargo.toml` although `dev` is excluded.
|
||||
"/dev-Cargo.toml",
|
||||
"/info.toml",
|
||||
"/README.md",
|
||||
"/LICENSE",
|
||||
]
|
||||
@@ -49,7 +51,6 @@ anyhow = "1"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
crossterm = { version = "0.29", default-features = false, features = ["windows", "events"] }
|
||||
notify = "8"
|
||||
rustlings-macros = { path = "rustlings-macros", version = "=6.5.0" }
|
||||
serde_json = "1"
|
||||
serde.workspace = true
|
||||
shlex = "2"
|
||||
@@ -59,6 +60,11 @@ wait-timeout = "0.2"
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
rustix = { version = "1.0", default-features = false, features = ["std", "stdio", "termios"] }
|
||||
|
||||
[build-dependencies]
|
||||
quote = "1"
|
||||
serde.workspace = true
|
||||
toml.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3"
|
||||
|
||||
|
||||
@@ -1,5 +1,98 @@
|
||||
use quote::quote;
|
||||
use serde::Deserialize;
|
||||
|
||||
fn main() {
|
||||
// Fix building from source on Windows because it can't handle file links.
|
||||
#[cfg(windows)]
|
||||
let _ = std::fs::copy("dev/Cargo.toml", "dev-Cargo.toml");
|
||||
|
||||
process_info_toml();
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ExerciseInfo<'a> {
|
||||
name: &'a str,
|
||||
dir: &'a str,
|
||||
#[serde(default)]
|
||||
input_files: Vec<&'a str>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct InfoFile<'a> {
|
||||
#[serde(borrow)]
|
||||
exercises: Vec<ExerciseInfo<'a>>,
|
||||
}
|
||||
|
||||
/// Translates the data in info.toml to Rust data structures to be embedded in
|
||||
/// the final binary.
|
||||
pub fn process_info_toml() {
|
||||
let info_file = include_str!("info.toml");
|
||||
let info = toml::de::from_str::<InfoFile>(info_file).expect("Failed to parse `info.toml`");
|
||||
let exercises = info.exercises;
|
||||
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
||||
|
||||
let exercise_files = exercises.iter().map(|exercise| {
|
||||
format!(
|
||||
"{manifest_dir}/exercises/{}/{}.rs",
|
||||
exercise.dir, exercise.name
|
||||
)
|
||||
});
|
||||
let solution_files = exercises.iter().map(|exercise| {
|
||||
format!(
|
||||
"{manifest_dir}/solutions/{}/{}.rs",
|
||||
exercise.dir, exercise.name
|
||||
)
|
||||
});
|
||||
|
||||
let mut dirs = Vec::with_capacity(32);
|
||||
let mut dir_inds = vec![0; exercises.len()];
|
||||
|
||||
for (exercise, dir_ind) in exercises.iter().zip(&mut dir_inds) {
|
||||
// The directory is often the last one inserted.
|
||||
if let Some(ind) = dirs.iter().rev().position(|dir| *dir == exercise.dir) {
|
||||
*dir_ind = dirs.len() - 1 - ind;
|
||||
continue;
|
||||
}
|
||||
|
||||
dirs.push(exercise.dir);
|
||||
*dir_ind = dirs.len() - 1;
|
||||
}
|
||||
|
||||
let input_files = exercises.iter().map(|exercise| {
|
||||
let names = exercise.input_files.iter();
|
||||
let paths = exercise
|
||||
.input_files
|
||||
.iter()
|
||||
.map(|f| format!("{manifest_dir}/exercises/{}/{}", exercise.dir, f));
|
||||
quote! {
|
||||
&[#(InputFile {
|
||||
name: #names,
|
||||
content: include_str!(#paths),
|
||||
}),*]
|
||||
}
|
||||
});
|
||||
|
||||
let readmes = dirs
|
||||
.iter()
|
||||
.map(|dir| format!("{manifest_dir}/exercises/{dir}/README.md"));
|
||||
|
||||
let embedded_files = quote! {
|
||||
EmbeddedFiles {
|
||||
info_file: #info_file,
|
||||
exercise_files: &[#(ExerciseFiles {
|
||||
exercise: include_bytes!(#exercise_files),
|
||||
solution: include_bytes!(#solution_files),
|
||||
dir_ind: #dir_inds,
|
||||
input_files: #input_files,
|
||||
}),*],
|
||||
exercise_dirs: &[#(ExerciseDir { name: #dirs, readme: include_bytes!(#readmes) }),*],
|
||||
}
|
||||
};
|
||||
|
||||
let out_dir = std::env::var("OUT_DIR").unwrap();
|
||||
std::fs::write(
|
||||
format!("{out_dir}/embedded_files.rs"),
|
||||
embedded_files.to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
[package]
|
||||
name = "rustlings-macros"
|
||||
description = "A macros crate intended to be used only by Rustlings"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
include = [
|
||||
"/src/",
|
||||
"/info.toml",
|
||||
]
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
quote = "1"
|
||||
serde.workspace = true
|
||||
toml.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -1,77 +0,0 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ExerciseInfo<'a> {
|
||||
name: &'a str,
|
||||
dir: &'a str,
|
||||
#[serde(default)]
|
||||
input_files: Vec<&'a str>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct InfoFile<'a> {
|
||||
#[serde(borrow)]
|
||||
exercises: Vec<ExerciseInfo<'a>>,
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn include_files(_: TokenStream) -> TokenStream {
|
||||
let info_file = include_str!("../info.toml");
|
||||
let info = toml::de::from_str::<InfoFile>(info_file).expect("Failed to parse `info.toml`");
|
||||
let exercises = info.exercises;
|
||||
|
||||
let exercise_files = exercises
|
||||
.iter()
|
||||
.map(|exercise| format!("../exercises/{}/{}.rs", exercise.dir, exercise.name));
|
||||
let solution_files = exercises
|
||||
.iter()
|
||||
.map(|exercise| format!("../solutions/{}/{}.rs", exercise.dir, exercise.name));
|
||||
|
||||
let mut dirs = Vec::with_capacity(32);
|
||||
let mut dir_inds = vec![0; exercises.len()];
|
||||
|
||||
for (exercise, dir_ind) in exercises.iter().zip(&mut dir_inds) {
|
||||
// The directory is often the last one inserted.
|
||||
if let Some(ind) = dirs.iter().rev().position(|dir| *dir == exercise.dir) {
|
||||
*dir_ind = dirs.len() - 1 - ind;
|
||||
continue;
|
||||
}
|
||||
|
||||
dirs.push(exercise.dir);
|
||||
*dir_ind = dirs.len() - 1;
|
||||
}
|
||||
|
||||
let input_files = exercises.iter().map(|exercise| {
|
||||
let names = exercise.input_files.iter();
|
||||
let paths = exercise
|
||||
.input_files
|
||||
.iter()
|
||||
.map(|f| format!("../exercises/{}/{}", exercise.dir, f));
|
||||
quote! {
|
||||
&[#(InputFile {
|
||||
name: #names,
|
||||
content: include_str!(#paths),
|
||||
}),*]
|
||||
}
|
||||
});
|
||||
|
||||
let readmes = dirs
|
||||
.iter()
|
||||
.map(|dir| format!("../exercises/{dir}/README.md"));
|
||||
|
||||
quote! {
|
||||
EmbeddedFiles {
|
||||
info_file: #info_file,
|
||||
exercise_files: &[#(ExerciseFiles {
|
||||
exercise: include_bytes!(#exercise_files),
|
||||
solution: include_bytes!(#solution_files),
|
||||
dir_ind: #dir_inds,
|
||||
input_files: #input_files,
|
||||
}),*],
|
||||
exercise_dirs: &[#(ExerciseDir { name: #dirs, readme: include_bytes!(#readmes) }),*],
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
+1
-1
@@ -7,7 +7,7 @@ use std::{
|
||||
use crate::info_file::ExerciseInfo;
|
||||
|
||||
/// Contains all embedded files.
|
||||
pub static EMBEDDED_FILES: EmbeddedFiles = rustlings_macros::include_files!();
|
||||
pub static EMBEDDED_FILES: EmbeddedFiles = include!(concat!(env!("OUT_DIR"), "/embedded_files.rs"));
|
||||
|
||||
// Files related to one exercise.
|
||||
pub struct ExerciseFiles {
|
||||
|
||||
Reference in New Issue
Block a user