|
1 | 1 | use regex::Regex;
|
2 | 2 | use serde::Deserialize;
|
3 |
| -use std::env; |
4 | 3 | use std::fmt::{self, Display, Formatter};
|
5 | 4 | use std::fs::{self, remove_file, File};
|
6 |
| -use std::io::Read; |
| 5 | +use std::io::{self, BufRead, BufReader}; |
7 | 6 | use std::path::PathBuf;
|
8 | 7 | use std::process::{self, Command};
|
| 8 | +use std::{array, env, mem}; |
9 | 9 |
|
10 | 10 | const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"];
|
11 | 11 | const RUSTC_EDITION_ARGS: &[&str] = &["--edition", "2021"];
|
12 | 12 | const RUSTC_NO_DEBUG_ARGS: &[&str] = &["-C", "strip=debuginfo"];
|
13 |
| -const I_AM_DONE_REGEX: &str = r"(?m)^\s*///?\s*I\s+AM\s+NOT\s+DONE"; |
| 13 | +const I_AM_DONE_REGEX: &str = r"^\s*///?\s*I\s+AM\s+NOT\s+DONE"; |
14 | 14 | const CONTEXT: usize = 2;
|
15 | 15 | const CLIPPY_CARGO_TOML_PATH: &str = "./exercises/22_clippy/Cargo.toml";
|
16 | 16 |
|
@@ -205,51 +205,89 @@ path = "{}.rs""#,
|
205 | 205 | }
|
206 | 206 |
|
207 | 207 | pub fn state(&self) -> State {
|
208 |
| - let mut source_file = File::open(&self.path).unwrap_or_else(|e| { |
| 208 | + let source_file = File::open(&self.path).unwrap_or_else(|e| { |
209 | 209 | panic!(
|
210 | 210 | "We were unable to open the exercise file {}! {e}",
|
211 | 211 | self.path.display()
|
212 | 212 | )
|
213 | 213 | });
|
214 |
| - |
215 |
| - let source = { |
216 |
| - let mut s = String::new(); |
217 |
| - source_file.read_to_string(&mut s).unwrap_or_else(|e| { |
218 |
| - panic!( |
219 |
| - "We were unable to read the exercise file {}! {e}", |
220 |
| - self.path.display() |
221 |
| - ) |
222 |
| - }); |
223 |
| - s |
| 214 | + let mut source_reader = BufReader::new(source_file); |
| 215 | + let mut read_line = |buf: &mut String| -> io::Result<_> { |
| 216 | + let n = source_reader.read_line(buf)?; |
| 217 | + if buf.ends_with('\n') { |
| 218 | + buf.pop(); |
| 219 | + if buf.ends_with('\r') { |
| 220 | + buf.pop(); |
| 221 | + } |
| 222 | + } |
| 223 | + Ok(n) |
224 | 224 | };
|
225 | 225 |
|
226 | 226 | let re = Regex::new(I_AM_DONE_REGEX).unwrap();
|
227 |
| - |
228 |
| - if !re.is_match(&source) { |
229 |
| - return State::Done; |
| 227 | + let mut matched_line_ind: usize = 0; |
| 228 | + let mut prev_lines: [_; CONTEXT] = array::from_fn(|_| String::with_capacity(256)); |
| 229 | + let mut line = String::with_capacity(256); |
| 230 | + |
| 231 | + loop { |
| 232 | + match read_line(&mut line) { |
| 233 | + Ok(0) => break, |
| 234 | + Ok(_) => { |
| 235 | + if re.is_match(&line) { |
| 236 | + let mut context = Vec::with_capacity(2 * CONTEXT + 1); |
| 237 | + for (ind, prev_line) in prev_lines |
| 238 | + .into_iter() |
| 239 | + .rev() |
| 240 | + .take(matched_line_ind) |
| 241 | + .enumerate() |
| 242 | + { |
| 243 | + context.push(ContextLine { |
| 244 | + line: prev_line, |
| 245 | + // TODO |
| 246 | + number: matched_line_ind - CONTEXT + ind + 1, |
| 247 | + important: false, |
| 248 | + }); |
| 249 | + } |
| 250 | + |
| 251 | + context.push(ContextLine { |
| 252 | + line, |
| 253 | + number: matched_line_ind + 1, |
| 254 | + important: true, |
| 255 | + }); |
| 256 | + |
| 257 | + for ind in 0..CONTEXT { |
| 258 | + let mut next_line = String::with_capacity(256); |
| 259 | + let Ok(n) = read_line(&mut next_line) else { |
| 260 | + break; |
| 261 | + }; |
| 262 | + |
| 263 | + if n == 0 { |
| 264 | + break; |
| 265 | + } |
| 266 | + |
| 267 | + context.push(ContextLine { |
| 268 | + line: next_line, |
| 269 | + number: matched_line_ind + ind + 2, |
| 270 | + important: false, |
| 271 | + }); |
| 272 | + } |
| 273 | + |
| 274 | + return State::Pending(context); |
| 275 | + } |
| 276 | + |
| 277 | + matched_line_ind += 1; |
| 278 | + for prev_line in &mut prev_lines { |
| 279 | + mem::swap(&mut line, prev_line); |
| 280 | + } |
| 281 | + line.clear(); |
| 282 | + } |
| 283 | + Err(e) => panic!( |
| 284 | + "We were unable to read the exercise file {}! {e}", |
| 285 | + self.path.display() |
| 286 | + ), |
| 287 | + } |
230 | 288 | }
|
231 | 289 |
|
232 |
| - let matched_line_index = source |
233 |
| - .lines() |
234 |
| - .enumerate() |
235 |
| - .find_map(|(i, line)| if re.is_match(line) { Some(i) } else { None }) |
236 |
| - .expect("This should not happen at all"); |
237 |
| - |
238 |
| - let min_line = ((matched_line_index as i32) - (CONTEXT as i32)).max(0) as usize; |
239 |
| - let max_line = matched_line_index + CONTEXT; |
240 |
| - |
241 |
| - let context = source |
242 |
| - .lines() |
243 |
| - .enumerate() |
244 |
| - .filter(|&(i, _)| i >= min_line && i <= max_line) |
245 |
| - .map(|(i, line)| ContextLine { |
246 |
| - line: line.to_string(), |
247 |
| - number: i + 1, |
248 |
| - important: i == matched_line_index, |
249 |
| - }) |
250 |
| - .collect(); |
251 |
| - |
252 |
| - State::Pending(context) |
| 290 | + State::Done |
253 | 291 | }
|
254 | 292 |
|
255 | 293 | // Check that the exercise looks to be solved using self.state()
|
|
0 commit comments