Skip to content

Commit d343ae3

Browse files
committed
write input files to disk before running exercise
1 parent c6c4dcd commit d343ae3

4 files changed

Lines changed: 44 additions & 7 deletions

File tree

src/app_state.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,11 @@ impl AppState {
8383
})?;
8484

8585
let dir_canonical_path = term::canonicalize("exercises");
86+
let official_exercises = !Path::new("info.toml").exists();
8687
let mut exercises = exercise_infos
8788
.into_iter()
88-
.map(|exercise_info| {
89+
.enumerate()
90+
.map(|(i, exercise_info)| {
8991
let canonical_path = dir_canonical_path.as_deref().map(|dir_canonical_path| {
9092
let mut canonical_path;
9193
if let Some(dir) = exercise_info.dir {
@@ -107,10 +109,16 @@ impl AppState {
107109
canonical_path.push_str(".rs");
108110
canonical_path
109111
});
112+
let embedded_input_files = if official_exercises {
113+
EMBEDDED_FILES.exercise_files[i].input_files
114+
} else {
115+
&[]
116+
};
110117

111118
Exercise {
112119
name: exercise_info.name,
113120
dir: exercise_info.dir,
121+
embedded_input_files,
114122
// LEAKING: For `Editor::open`. The app state is used until the end of the program.
115123
path: exercise_info.path().leak(),
116124
canonical_path,
@@ -175,7 +183,7 @@ impl AppState {
175183
final_message,
176184
state_file,
177185
file_buf,
178-
official_exercises: !Path::new("info.toml").exists(),
186+
official_exercises,
179187
cmd_runner,
180188
// VS Code has its own file link handling
181189
emit_file_links: !vs_code_term,
@@ -612,6 +620,7 @@ mod tests {
612620
Exercise {
613621
name: "0",
614622
dir: None,
623+
embedded_input_files: &[],
615624
path: "exercises/0.rs",
616625
canonical_path: None,
617626
test: false,

src/embedded.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ use crate::info_file::ExerciseInfo;
1010
pub static EMBEDDED_FILES: EmbeddedFiles = rustlings_macros::include_files!();
1111

1212
// Files related to one exercise.
13-
struct ExerciseFiles {
13+
pub struct ExerciseFiles {
1414
// The content of the exercise file.
1515
exercise: &'static [u8],
1616
// The content of the solution file.
1717
solution: &'static [u8],
1818
// Index of the related `ExerciseDir` in `EmbeddedFiles::exercise_dirs`.
1919
dir_ind: usize,
2020
// Files that are read by the exercise.
21-
input_files: &'static [InputFile],
21+
pub input_files: &'static [InputFile],
2222
}
2323

2424
// Input files that may be read by exercises.
@@ -64,7 +64,7 @@ impl ExerciseDir {
6464
pub struct EmbeddedFiles {
6565
/// The content of the `info.toml` file.
6666
pub info_file: &'static str,
67-
exercise_files: &'static [ExerciseFiles],
67+
pub exercise_files: &'static [ExerciseFiles],
6868
pub exercise_dirs: &'static [ExerciseDir],
6969
}
7070

src/exercise.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use anyhow::Result;
1+
use anyhow::{Context, Result};
22
use crossterm::{
33
QueueableCommand,
44
style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor},
@@ -7,6 +7,7 @@ use std::io::{self, StdoutLock, Write};
77

88
use crate::{
99
cmd::CmdRunner,
10+
embedded::InputFile,
1011
term::{self, CountedWrite, file_path, terminal_file_link, write_ansi},
1112
};
1213

@@ -69,6 +70,7 @@ fn run_bin(
6970
pub struct Exercise {
7071
pub name: &'static str,
7172
pub dir: Option<&'static str>,
73+
pub embedded_input_files: &'static [InputFile],
7274
/// Path of the exercise file starting with the `exercises/` directory.
7375
pub path: &'static str,
7476
pub canonical_path: Option<String>,
@@ -99,6 +101,7 @@ pub trait RunnableExercise {
99101
fn dir(&self) -> Option<&str>;
100102
fn strict_clippy(&self) -> bool;
101103
fn test(&self) -> bool;
104+
fn embedded_input_files(&self) -> &[InputFile];
102105

103106
// Compile, check and run the exercise or its solution (depending on `bin_name´).
104107
// The output is written to the `output` buffer after clearing it.
@@ -112,6 +115,19 @@ pub trait RunnableExercise {
112115
output.clear();
113116
}
114117

118+
// Input files are already written to disk during `rustlings init`.
119+
// We repeat it here to ensure an exercise doesn't fail because the
120+
// input files were deleted or modified in the meantime. Note that
121+
// `self.embedded_input_files()` is empty for community exercises.
122+
for input_file in self.embedded_input_files() {
123+
let name = input_file.name;
124+
let path = match self.dir() {
125+
Some(dir) => format!("exercises/{dir}/{name}"),
126+
None => format!("exercises/{name}"),
127+
};
128+
std::fs::write(path, input_file.content).context("failed to write input file")?;
129+
}
130+
115131
let build_success = cmd_runner
116132
.cargo("build", bin_name, output.as_deref_mut())
117133
.run("cargo build …")?;
@@ -224,4 +240,8 @@ impl RunnableExercise for Exercise {
224240
fn test(&self) -> bool {
225241
self.test
226242
}
243+
244+
fn embedded_input_files(&self) -> &[InputFile] {
245+
self.embedded_input_files
246+
}
227247
}

src/info_file.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ use anyhow::{Context, Error, Result, bail};
22
use serde::Deserialize;
33
use std::{fs, io::ErrorKind};
44

5-
use crate::{embedded::EMBEDDED_FILES, exercise::RunnableExercise};
5+
use crate::{
6+
embedded::{EMBEDDED_FILES, InputFile},
7+
exercise::RunnableExercise,
8+
};
69

710
/// Deserialized from the `info.toml` file.
811
#[derive(Deserialize)]
@@ -72,6 +75,11 @@ impl RunnableExercise for ExerciseInfo<'_> {
7275
fn test(&self) -> bool {
7376
self.test
7477
}
78+
79+
fn embedded_input_files(&self) -> &[InputFile] {
80+
// We don't have the input files embedded for communtiy exercises.
81+
&[]
82+
}
7583
}
7684

7785
/// The deserialized `info.toml` file.

0 commit comments

Comments
 (0)