diff options
Diffstat (limited to 'src/project.rs')
| -rw-r--r-- | src/project.rs | 138 |
1 files changed, 61 insertions, 77 deletions
diff --git a/src/project.rs b/src/project.rs index bcbd7ad..0f56de9 100644 --- a/src/project.rs +++ b/src/project.rs @@ -1,99 +1,83 @@ -use glob::glob; -use serde::{Deserialize, Serialize}; +use anyhow::{Context, Result}; +use serde::Serialize; use std::env; -use std::error::Error; use std::path::PathBuf; -use std::process::Command; +use std::process::{Command, Stdio}; + +use crate::exercise::Exercise; /// Contains the structure of resulting rust-project.json file /// and functions to build the data required to create the file -#[derive(Serialize, Deserialize)] -pub struct RustAnalyzerProject { - sysroot_src: String, - pub crates: Vec<Crate>, +#[derive(Serialize)] +struct RustAnalyzerProject { + sysroot_src: PathBuf, + crates: Vec<Crate>, } -#[derive(Serialize, Deserialize)] -pub struct Crate { - root_module: String, - edition: String, - deps: Vec<String>, - cfg: Vec<String>, +#[derive(Serialize)] +struct Crate { + root_module: PathBuf, + edition: &'static str, + // Not used, but required in the JSON file. + deps: Vec<()>, + // Only `test` is used for all crates. + // Therefore, an array is used instead of a `Vec`. + cfg: [&'static str; 1], } impl RustAnalyzerProject { - pub fn new() -> RustAnalyzerProject { - RustAnalyzerProject { - sysroot_src: String::new(), - crates: Vec::new(), - } - } - - /// Write rust-project.json to disk - pub fn write_to_disk(&self) -> Result<(), std::io::Error> { - std::fs::write( - "./rust-project.json", - serde_json::to_vec(&self).expect("Failed to serialize to JSON"), - )?; - Ok(()) - } + fn build(exercises: Vec<Exercise>) -> Result<Self> { + let crates = exercises + .into_iter() + .map(|exercise| Crate { + root_module: exercise.path, + edition: "2021", + deps: Vec::new(), + // This allows rust_analyzer to work inside `#[test]` blocks + cfg: ["test"], + }) + .collect(); - /// If path contains .rs extension, add a crate to `rust-project.json` - fn path_to_json(&mut self, path: PathBuf) -> Result<(), Box<dyn Error>> { - if let Some(ext) = path.extension() { - if ext == "rs" { - self.crates.push(Crate { - root_module: path.display().to_string(), - edition: "2021".to_string(), - deps: Vec::new(), - // This allows rust_analyzer to work inside #[test] blocks - cfg: vec!["test".to_string()], - }) - } - } - - Ok(()) - } - - /// Parse the exercises folder for .rs files, any matches will create - /// a new `crate` in rust-project.json which allows rust-analyzer to - /// treat it like a normal binary - pub fn exercises_to_json(&mut self) -> Result<(), Box<dyn Error>> { - for path in glob("./exercises/**/*")? { - self.path_to_json(path?)?; - } - Ok(()) - } - - /// Use `rustc` to determine the default toolchain - pub fn get_sysroot_src(&mut self) -> Result<(), Box<dyn Error>> { - // check if RUST_SRC_PATH is set - if let Ok(path) = env::var("RUST_SRC_PATH") { - self.sysroot_src = path; - return Ok(()); + if let Some(path) = env::var_os("RUST_SRC_PATH") { + return Ok(Self { + sysroot_src: PathBuf::from(path), + crates, + }); } let toolchain = Command::new("rustc") .arg("--print") .arg("sysroot") - .output()? + .stderr(Stdio::inherit()) + .output() + .context("Failed to get the sysroot from `rustc`. Do you have `rustc` installed?")? .stdout; - let toolchain = String::from_utf8_lossy(&toolchain); - let mut whitespace_iter = toolchain.split_whitespace(); - - let toolchain = whitespace_iter.next().unwrap_or(&toolchain); + let toolchain = + String::from_utf8(toolchain).context("The toolchain path is invalid UTF8")?; + let toolchain = toolchain.trim_end(); + println!("Determined toolchain: {toolchain}\n"); - println!("Determined toolchain: {}\n", &toolchain); + let mut sysroot_src = PathBuf::with_capacity(256); + sysroot_src.extend([toolchain, "lib", "rustlib", "src", "rust", "library"]); - self.sysroot_src = (std::path::Path::new(toolchain) - .join("lib") - .join("rustlib") - .join("src") - .join("rust") - .join("library") - .to_string_lossy()) - .to_string(); - Ok(()) + Ok(Self { + sysroot_src, + crates, + }) } } + +/// Write `rust-project.json` to disk. +pub fn write_project_json(exercises: Vec<Exercise>) -> Result<()> { + let content = RustAnalyzerProject::build(exercises)?; + + // Using the capacity 2^14 since the file length in bytes is higher than 2^13. + // The final length is not known exactly because it depends on the user's sysroot path, + // the current number of exercises etc. + let mut buf = Vec::with_capacity(1 << 14); + serde_json::to_writer(&mut buf, &content)?; + std::fs::write("rust-project.json", buf)?; + + Ok(()) +} |
