Spawn a command, wait for it with a timeout, and return the result. Both stdout and stderr are captured. PHPStan writes its JSON output to stdout.
(
command: &mut Command,
timeout: Duration,
cancelled: &std::sync::atomic::AtomicBool,
)
| 438 | /// Both stdout and stderr are captured. PHPStan writes its JSON |
| 439 | /// output to stdout. |
| 440 | fn run_command_with_timeout( |
| 441 | command: &mut Command, |
| 442 | timeout: Duration, |
| 443 | cancelled: &std::sync::atomic::AtomicBool, |
| 444 | ) -> Result<CommandOutput, String> { |
| 445 | let mut child = command |
| 446 | .stdout(Stdio::piped()) |
| 447 | .stderr(Stdio::piped()) |
| 448 | .spawn() |
| 449 | .map_err(|e| format!("Failed to spawn PHPStan: {}", e))?; |
| 450 | |
| 451 | let start = std::time::Instant::now(); |
| 452 | loop { |
| 453 | match child.try_wait() { |
| 454 | Ok(Some(status)) => { |
| 455 | let stdout = child |
| 456 | .stdout |
| 457 | .take() |
| 458 | .and_then(|mut s| { |
| 459 | let mut buf = String::new(); |
| 460 | std::io::Read::read_to_string(&mut s, &mut buf).ok()?; |
| 461 | Some(buf) |
| 462 | }) |
| 463 | .unwrap_or_default(); |
| 464 | |
| 465 | let stderr = child |
| 466 | .stderr |
| 467 | .take() |
| 468 | .and_then(|mut s| { |
| 469 | let mut buf = String::new(); |
| 470 | std::io::Read::read_to_string(&mut s, &mut buf).ok()?; |
| 471 | Some(buf) |
| 472 | }) |
| 473 | .unwrap_or_default(); |
| 474 | |
| 475 | return Ok(CommandOutput { |
| 476 | code: status.code().unwrap_or(-1), |
| 477 | stdout, |
| 478 | stderr, |
| 479 | }); |
| 480 | } |
| 481 | Ok(None) => { |
| 482 | if start.elapsed() >= timeout { |
| 483 | let _ = child.kill(); |
| 484 | let _ = child.wait(); |
| 485 | return Err(format!("PHPStan timed out after {}ms", timeout.as_millis())); |
| 486 | } |
| 487 | if cancelled.load(std::sync::atomic::Ordering::Acquire) { |
| 488 | let _ = child.kill(); |
| 489 | let _ = child.wait(); |
| 490 | return Err("PHPStan cancelled (server shutting down)".to_string()); |
| 491 | } |
| 492 | std::thread::sleep(Duration::from_millis(50)); |
| 493 | } |
| 494 | Err(e) => { |
| 495 | let _ = child.kill(); |
| 496 | return Err(format!("Error waiting for PHPStan: {}", e)); |
| 497 | } |
no test coverage detected