This commit is contained in:
mo8it 2024-03-28 22:11:16 +01:00
parent 5b4103bbac
commit 3ff9b0cd2a
5 changed files with 74 additions and 54 deletions

View file

@ -52,7 +52,9 @@ impl EmbeddedFlatDir {
} }
} }
self.readme.write_to_disk(WriteStrategy::Overwrite) self.readme.write_to_disk(WriteStrategy::Overwrite)?;
Ok(())
} }
} }
@ -63,16 +65,31 @@ struct ExercisesDir {
} }
pub struct EmbeddedFiles { pub struct EmbeddedFiles {
info_toml_content: &'static str, pub info_toml_content: &'static str,
exercises_dir: ExercisesDir, exercises_dir: ExercisesDir,
} }
impl EmbeddedFiles { impl EmbeddedFiles {
pub fn init_exercises_dir(&self) -> io::Result<()> { pub fn init_exercises_dir(&self) -> io::Result<()> {
create_dir("exercises")?; create_dir("exercises")?;
self.exercises_dir self.exercises_dir
.readme .readme
.write_to_disk(WriteStrategy::Overwrite) .write_to_disk(WriteStrategy::IfNotExists)?;
for file in self.exercises_dir.files {
file.write_to_disk(WriteStrategy::IfNotExists)?;
}
for dir in self.exercises_dir.dirs {
dir.init_on_disk()?;
for file in dir.content {
file.write_to_disk(WriteStrategy::IfNotExists)?;
}
}
Ok(())
} }
pub fn write_exercise_to_disk(&self, path: &Path, strategy: WriteStrategy) -> io::Result<()> { pub fn write_exercise_to_disk(&self, path: &Path, strategy: WriteStrategy) -> io::Result<()> {

View file

@ -36,7 +36,7 @@ fn temp_file() -> String {
.filter(|c| c.is_alphanumeric()) .filter(|c| c.is_alphanumeric())
.collect(); .collect();
format!("temp_{}_{thread_id}", process::id()) format!("./temp_{}_{thread_id}", process::id())
} }
// The mode of the exercise. // The mode of the exercise.

View file

@ -5,14 +5,14 @@ use crate::verify::verify;
use anyhow::Result; use anyhow::Result;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use console::Emoji; use console::Emoji;
use embedded::EMBEDDED_FILES;
use notify_debouncer_mini::notify::{self, RecursiveMode}; use notify_debouncer_mini::notify::{self, RecursiveMode};
use notify_debouncer_mini::{new_debouncer, DebouncedEventKind}; use notify_debouncer_mini::{new_debouncer, DebouncedEventKind};
use shlex::Shlex; use shlex::Shlex;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fs; use std::io::{self, prelude::*, stdin, stdout};
use std::io::{self, prelude::*};
use std::path::Path; use std::path::Path;
use std::process::Command; use std::process::{exit, Command};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{channel, RecvTimeoutError}; use std::sync::mpsc::{channel, RecvTimeoutError};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -54,7 +54,7 @@ enum Subcommands {
/// The name of the exercise /// The name of the exercise
name: String, name: String,
}, },
/// Reset a single exercise using "git stash -- <filename>" /// Reset a single exercise
Reset { Reset {
/// The name of the exercise /// The name of the exercise
name: String, name: String,
@ -83,13 +83,45 @@ enum Subcommands {
#[arg(short, long)] #[arg(short, long)]
solved: bool, solved: bool,
}, },
/// Enable rust-analyzer for exercises
Lsp,
} }
fn main() -> Result<()> { fn main() -> Result<()> {
let args = Args::parse(); let args = Args::parse();
let exercises = toml_edit::de::from_str::<ExerciseList>(EMBEDDED_FILES.info_toml_content)
.unwrap()
.exercises;
if !Path::new("exercises").is_dir() {
let mut stdout = stdout().lock();
write!(
stdout,
"The `exercises` directory wasn't found in the current directory.
Do you want to initialize Rustlings in the current directory (y/n)? "
)?;
stdout.flush()?;
let mut answer = String::new();
stdin().read_line(&mut answer)?;
answer.make_ascii_lowercase();
if answer.trim() != "y" {
exit(1);
}
EMBEDDED_FILES.init_exercises_dir()?;
if let Err(e) = write_project_json(&exercises) {
writeln!(
stdout,
"Failed to write rust-project.json to disk for rust-analyzer: {e}"
)?;
} else {
writeln!(stdout, "Successfully generated rust-project.json")?;
writeln!(
stdout,
"rust-analyzer will now parse exercises, restart your language server or editor"
)?;
}
}
if args.command.is_none() { if args.command.is_none() {
println!("\n{WELCOME}\n"); println!("\n{WELCOME}\n");
} }
@ -101,18 +133,6 @@ fn main() -> Result<()> {
std::process::exit(1); std::process::exit(1);
} }
let info_file = fs::read_to_string("info.toml").unwrap_or_else(|e| {
match e.kind() {
io::ErrorKind::NotFound => println!(
"The program must be run from the rustlings directory\nTry `cd rustlings/`!",
),
_ => println!("Failed to read the info.toml file: {e}"),
}
std::process::exit(1);
});
let exercises = toml_edit::de::from_str::<ExerciseList>(&info_file)
.unwrap()
.exercises;
let verbose = args.nocapture; let verbose = args.nocapture;
let command = args.command.unwrap_or_else(|| { let command = args.command.unwrap_or_else(|| {
@ -205,7 +225,7 @@ fn main() -> Result<()> {
Subcommands::Reset { name } => { Subcommands::Reset { name } => {
let exercise = find_exercise(&name, &exercises); let exercise = find_exercise(&name, &exercises);
reset(exercise).unwrap_or_else(|_| std::process::exit(1)); reset(exercise)?;
} }
Subcommands::Hint { name } => { Subcommands::Hint { name } => {
@ -219,15 +239,6 @@ fn main() -> Result<()> {
.unwrap_or_else(|_| std::process::exit(1)); .unwrap_or_else(|_| std::process::exit(1));
} }
Subcommands::Lsp => {
if let Err(e) = write_project_json(exercises) {
println!("Failed to write rust-project.json to disk for rust-analyzer: {e}");
} else {
println!("Successfully generated rust-project.json");
println!("rust-analyzer will now parse exercises, restart your language server or editor");
}
}
Subcommands::Watch { success_hints } => match watch(&exercises, verbose, success_hints) { Subcommands::Watch { success_hints } => match watch(&exercises, verbose, success_hints) {
Err(e) => { Err(e) => {
println!("Error: Could not watch your progress. Error message was {e:?}."); println!("Error: Could not watch your progress. Error message was {e:?}.");

View file

@ -1,7 +1,7 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use serde::Serialize; use serde::Serialize;
use std::env; use std::env;
use std::path::PathBuf; use std::path::{Path, PathBuf};
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
use crate::exercise::Exercise; use crate::exercise::Exercise;
@ -9,14 +9,14 @@ use crate::exercise::Exercise;
/// Contains the structure of resulting rust-project.json file /// Contains the structure of resulting rust-project.json file
/// and functions to build the data required to create the file /// and functions to build the data required to create the file
#[derive(Serialize)] #[derive(Serialize)]
struct RustAnalyzerProject { struct RustAnalyzerProject<'a> {
sysroot_src: PathBuf, sysroot_src: PathBuf,
crates: Vec<Crate>, crates: Vec<Crate<'a>>,
} }
#[derive(Serialize)] #[derive(Serialize)]
struct Crate { struct Crate<'a> {
root_module: PathBuf, root_module: &'a Path,
edition: &'static str, edition: &'static str,
// Not used, but required in the JSON file. // Not used, but required in the JSON file.
deps: Vec<()>, deps: Vec<()>,
@ -25,12 +25,12 @@ struct Crate {
cfg: [&'static str; 1], cfg: [&'static str; 1],
} }
impl RustAnalyzerProject { impl<'a> RustAnalyzerProject<'a> {
fn build(exercises: Vec<Exercise>) -> Result<Self> { fn build(exercises: &'a [Exercise]) -> Result<Self> {
let crates = exercises let crates = exercises
.into_iter() .iter()
.map(|exercise| Crate { .map(|exercise| Crate {
root_module: exercise.path, root_module: &exercise.path,
edition: "2021", edition: "2021",
deps: Vec::new(), deps: Vec::new(),
// This allows rust_analyzer to work inside `#[test]` blocks // This allows rust_analyzer to work inside `#[test]` blocks
@ -69,7 +69,7 @@ impl RustAnalyzerProject {
} }
/// Write `rust-project.json` to disk. /// Write `rust-project.json` to disk.
pub fn write_project_json(exercises: Vec<Exercise>) -> Result<()> { pub fn write_project_json(exercises: &[Exercise]) -> Result<()> {
let content = RustAnalyzerProject::build(exercises)?; let content = RustAnalyzerProject::build(exercises)?;
// Using the capacity 2^14 since the file length in bytes is higher than 2^13. // Using the capacity 2^14 since the file length in bytes is higher than 2^13.

View file

@ -1,6 +1,7 @@
use std::process::Command; use std::io;
use std::time::Duration; use std::time::Duration;
use crate::embedded::{WriteStrategy, EMBEDDED_FILES};
use crate::exercise::{Exercise, Mode}; use crate::exercise::{Exercise, Mode};
use crate::verify::test; use crate::verify::test;
use indicatif::ProgressBar; use indicatif::ProgressBar;
@ -19,17 +20,8 @@ pub fn run(exercise: &Exercise, verbose: bool) -> Result<(), ()> {
} }
// Resets the exercise by stashing the changes. // Resets the exercise by stashing the changes.
pub fn reset(exercise: &Exercise) -> Result<(), ()> { pub fn reset(exercise: &Exercise) -> io::Result<()> {
let command = Command::new("git") EMBEDDED_FILES.write_exercise_to_disk(&exercise.path, WriteStrategy::Overwrite)
.arg("stash")
.arg("--")
.arg(&exercise.path)
.spawn();
match command {
Ok(_) => Ok(()),
Err(_) => Err(()),
}
} }
// Invoke the rust compiler on the path of the given exercise // Invoke the rust compiler on the path of the given exercise