Skip to content

Commit 0cbcb89

Browse files
committed
Borrow deserialized values
1 parent 1356420 commit 0cbcb89

10 files changed

Lines changed: 57 additions & 54 deletions

File tree

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ rust-version = "1.88"
1919

2020
[workspace.dependencies]
2121
serde = { version = "1", features = ["derive"] }
22-
toml = { version = "0.9", default-features = false, features = ["std", "parse", "serde"] }
22+
toml = { version = "1", default-features = false, features = ["std", "parse", "serde"] }
2323

2424
[package]
2525
name = "rustlings"

rustlings-macros/src/lib.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ use quote::quote;
33
use serde::Deserialize;
44

55
#[derive(Deserialize)]
6-
struct ExerciseInfo {
7-
name: String,
8-
dir: String,
6+
struct ExerciseInfo<'a> {
7+
name: &'a str,
8+
dir: &'a str,
99
}
1010

1111
#[derive(Deserialize)]
12-
struct InfoFile {
13-
exercises: Vec<ExerciseInfo>,
12+
struct InfoFile<'a> {
13+
#[serde(borrow)]
14+
exercises: Vec<ExerciseInfo<'a>>,
1415
}
1516

1617
#[proc_macro]
@@ -37,7 +38,7 @@ pub fn include_files(_: TokenStream) -> TokenStream {
3738
continue;
3839
}
3940

40-
dirs.push(exercise.dir.as_str());
41+
dirs.push(exercise.dir);
4142
*dir_ind = dirs.len() - 1;
4243
}
4344

src/app_state.rs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pub struct AppState {
5454
exercises: Vec<Exercise>,
5555
// Caches the number of done exercises to avoid iterating over all exercises every time.
5656
n_done: u16,
57-
final_message: String,
57+
final_message: &'static str,
5858
state_file: File,
5959
// Preallocated buffer for reading and writing the state file.
6060
file_buf: Vec<u8>,
@@ -66,7 +66,7 @@ pub struct AppState {
6666
impl AppState {
6767
pub fn new(
6868
exercise_infos: Vec<ExerciseInfo>,
69-
final_message: String,
69+
final_message: &'static str,
7070
) -> Result<(Self, StateFileStatus)> {
7171
let cmd_runner = CmdRunner::build()?;
7272
let mut state_file = OpenOptions::new()
@@ -87,34 +87,33 @@ impl AppState {
8787
// Leaking is not a problem because the `AppState` instance lives until
8888
// the end of the program.
8989
let path = exercise_info.path().leak();
90-
let name = exercise_info.name.leak();
91-
let dir = exercise_info.dir.map(|dir| &*dir.leak());
92-
let hint = exercise_info.hint.leak().trim_ascii();
90+
let hint = exercise_info.hint.trim_ascii();
9391

9492
let canonical_path = dir_canonical_path.as_deref().map(|dir_canonical_path| {
9593
let mut canonical_path;
96-
if let Some(dir) = dir {
94+
if let Some(dir) = exercise_info.dir {
9795
canonical_path = String::with_capacity(
98-
2 + dir_canonical_path.len() + dir.len() + name.len(),
96+
2 + dir_canonical_path.len() + dir.len() + exercise_info.name.len(),
9997
);
10098
canonical_path.push_str(dir_canonical_path);
10199
canonical_path.push_str(MAIN_SEPARATOR_STR);
102100
canonical_path.push_str(dir);
103101
} else {
104-
canonical_path =
105-
String::with_capacity(1 + dir_canonical_path.len() + name.len());
102+
canonical_path = String::with_capacity(
103+
1 + dir_canonical_path.len() + exercise_info.name.len(),
104+
);
106105
canonical_path.push_str(dir_canonical_path);
107106
}
108107

109108
canonical_path.push_str(MAIN_SEPARATOR_STR);
110-
canonical_path.push_str(name);
109+
canonical_path.push_str(exercise_info.name);
111110
canonical_path.push_str(".rs");
112111
canonical_path
113112
});
114113

115114
Exercise {
116-
dir,
117-
name,
115+
dir: exercise_info.dir,
116+
name: exercise_info.name,
118117
path,
119118
canonical_path,
120119
test: exercise_info.test,
@@ -616,7 +615,7 @@ mod tests {
616615
current_exercise_ind: 0,
617616
exercises: vec![dummy_exercise(), dummy_exercise(), dummy_exercise()],
618617
n_done: 0,
619-
final_message: String::new(),
618+
final_message: "",
620619
state_file: tempfile::tempfile().unwrap(),
621620
file_buf: Vec::new(),
622621
official_exercises: true,

src/cargo_toml.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub fn append_bins(
3838
buf.extend_from_slice(b"\", path = \"");
3939
buf.extend_from_slice(exercise_path_prefix);
4040
buf.extend_from_slice(b"exercises/");
41-
if let Some(dir) = &exercise_info.dir {
41+
if let Some(dir) = exercise_info.dir {
4242
buf.extend_from_slice(dir.as_bytes());
4343
buf.push(b'/');
4444
}
@@ -56,7 +56,7 @@ pub fn append_bins(
5656
buf.extend_from_slice(b"\", path = \"");
5757
buf.extend_from_slice(exercise_path_prefix);
5858
buf.extend_from_slice(b"solutions/");
59-
if let Some(dir) = &exercise_info.dir {
59+
if let Some(dir) = exercise_info.dir {
6060
buf.extend_from_slice(dir.as_bytes());
6161
buf.push(b'/');
6262
}
@@ -106,19 +106,19 @@ mod tests {
106106
fn test_bins() {
107107
let exercise_infos = [
108108
ExerciseInfo {
109-
name: String::from("1"),
109+
name: "1",
110110
dir: None,
111111
test: true,
112112
strict_clippy: true,
113-
hint: String::new(),
113+
hint: "",
114114
skip_check_unsolved: false,
115115
},
116116
ExerciseInfo {
117-
name: String::from("2"),
118-
dir: Some(String::from("d")),
117+
name: "2",
118+
dir: Some("d"),
119119
test: false,
120120
strict_clippy: false,
121-
hint: String::new(),
121+
hint: "",
122122
skip_check_unsolved: false,
123123
},
124124
];

src/dev/check.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result<HashSet<PathBuf>> {
6363

6464
let mut file_buf = String::with_capacity(1 << 14);
6565
for exercise_info in &info_file.exercises {
66-
let name = exercise_info.name.as_str();
66+
let name = exercise_info.name;
6767
if name.is_empty() {
6868
bail!("Found an empty exercise name in `info.toml`");
6969
}
@@ -76,7 +76,7 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result<HashSet<PathBuf>> {
7676
bail!("Char `{c}` in the exercise name `{name}` is not allowed");
7777
}
7878

79-
if let Some(dir) = &exercise_info.dir {
79+
if let Some(dir) = exercise_info.dir {
8080
if dir.is_empty() {
8181
bail!("The exercise `{name}` has an empty dir name in `info.toml`");
8282
}
@@ -214,7 +214,7 @@ fn check_exercises_unsolved(
214214
Some(
215215
thread::Builder::new()
216216
.spawn(|| exercise_info.run_exercise(None, cmd_runner))
217-
.map(|handle| (exercise_info.name.as_str(), handle)),
217+
.map(|handle| (exercise_info.name, handle)),
218218
)
219219
})
220220
.collect::<Result<Vec<_>, _>>()

src/embedded.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ impl EmbeddedFiles {
8585
exercise_path.truncate(prefix.len());
8686
exercise_path.push_str(dir.name);
8787
exercise_path.push('/');
88-
exercise_path.push_str(&exercise_info.name);
88+
exercise_path.push_str(exercise_info.name);
8989
exercise_path.push_str(".rs");
9090

9191
fs::write(&exercise_path, exercise_files.exercise)
@@ -141,13 +141,14 @@ mod tests {
141141
use super::*;
142142

143143
#[derive(Deserialize)]
144-
struct ExerciseInfo {
145-
dir: String,
144+
struct ExerciseInfo<'a> {
145+
dir: &'a str,
146146
}
147147

148148
#[derive(Deserialize)]
149-
struct InfoFile {
150-
exercises: Vec<ExerciseInfo>,
149+
struct InfoFile<'a> {
150+
#[serde(borrow)]
151+
exercises: Vec<ExerciseInfo<'a>>,
151152
}
152153

153154
#[test]

src/info_file.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@ use crate::{embedded::EMBEDDED_FILES, exercise::RunnableExercise};
88
#[derive(Deserialize)]
99
pub struct ExerciseInfo {
1010
/// Exercise's unique name.
11-
pub name: String,
11+
pub name: &'static str,
1212
/// Exercise's directory name inside the `exercises/` directory.
13-
pub dir: Option<String>,
13+
pub dir: Option<&'static str>,
1414
/// Run `cargo test` on the exercise.
1515
#[serde(default = "default_true")]
1616
pub test: bool,
1717
/// Deny all Clippy warnings.
1818
#[serde(default)]
1919
pub strict_clippy: bool,
2020
/// The exercise's hint to be shown to the user on request.
21-
pub hint: String,
21+
pub hint: &'static str,
2222
/// The exercise is already solved. Ignore it when checking that all exercises are unsolved.
2323
#[serde(default)]
2424
pub skip_check_unsolved: bool,
@@ -31,7 +31,7 @@ const fn default_true() -> bool {
3131
impl ExerciseInfo {
3232
/// Path to the exercise file starting with the `exercises/` directory.
3333
pub fn path(&self) -> String {
34-
let mut path = if let Some(dir) = &self.dir {
34+
let mut path = if let Some(dir) = self.dir {
3535
// 14 = 10 + 1 + 3
3636
// exercises/ + / + .rs
3737
let mut path = String::with_capacity(14 + dir.len() + self.name.len());
@@ -47,7 +47,7 @@ impl ExerciseInfo {
4747
path
4848
};
4949

50-
path.push_str(&self.name);
50+
path.push_str(self.name);
5151
path.push_str(".rs");
5252

5353
path
@@ -57,12 +57,12 @@ impl ExerciseInfo {
5757
impl RunnableExercise for ExerciseInfo {
5858
#[inline]
5959
fn name(&self) -> &str {
60-
&self.name
60+
self.name
6161
}
6262

6363
#[inline]
6464
fn dir(&self) -> Option<&str> {
65-
self.dir.as_deref()
65+
self.dir
6666
}
6767

6868
#[inline]
@@ -82,9 +82,9 @@ pub struct InfoFile {
8282
/// For possible breaking changes in the future for community exercises.
8383
pub format_version: u8,
8484
/// Shown to users when starting with the exercises.
85-
pub welcome_message: Option<String>,
85+
pub welcome_message: Option<&'static str>,
8686
/// Shown to users after finishing all exercises.
87-
pub final_message: Option<String>,
87+
pub final_message: Option<&'static str>,
8888
/// List of all exercises.
8989
pub exercises: Vec<ExerciseInfo>,
9090
}
@@ -95,7 +95,8 @@ impl InfoFile {
9595
pub fn parse() -> Result<Self> {
9696
// Read a local `info.toml` if it exists.
9797
let slf = match fs::read_to_string("info.toml") {
98-
Ok(file_content) => toml::de::from_str::<Self>(&file_content)
98+
// Leaking is fine since `InfoFile` is used until the end of the program.
99+
Ok(file_content) => toml::de::from_str::<Self>(file_content.leak())
99100
.context("Failed to parse the `info.toml` file")?,
100101
Err(e) => {
101102
if e.kind() == ErrorKind::NotFound {

src/init.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::{
88
env::set_current_dir,
99
fs::{self, create_dir},
1010
io::{self, Write},
11-
path::{Path, PathBuf},
11+
path::Path,
1212
process::{Command, Stdio},
1313
};
1414

@@ -18,8 +18,9 @@ use crate::{
1818
};
1919

2020
#[derive(Deserialize)]
21-
struct CargoLocateProject {
22-
root: PathBuf,
21+
struct CargoLocateProject<'a> {
22+
#[serde(borrow)]
23+
root: &'a Path,
2324
}
2425

2526
pub fn init() -> Result<()> {
@@ -72,7 +73,7 @@ pub fn init() -> Result<()> {
7273
)?
7374
.root;
7475

75-
let workspace_manifest_content = fs::read_to_string(&workspace_manifest)
76+
let workspace_manifest_content = fs::read_to_string(workspace_manifest)
7677
.with_context(|| format!("Failed to read the file {}", workspace_manifest.display()))?;
7778
if !workspace_manifest_content.contains("[workspace]")
7879
&& !workspace_manifest_content.contains("workspace.")

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ fn main() -> Result<ExitCode> {
128128
None
129129
} else {
130130
// For the notify event handler thread.
131-
// Leaking is not a problem because the slice lives until the end of the program.
131+
// Leaking is fine since the slice is used until the end of the program.
132132
Some(
133133
&*app_state
134134
.exercises()

0 commit comments

Comments
 (0)