first commit

This commit is contained in:
Jana Dönszelmann 2026-05-16 20:30:31 +02:00
commit 4cd2267497
No known key found for this signature in database
10 changed files with 644 additions and 0 deletions

168
src/main.rs Normal file
View file

@ -0,0 +1,168 @@
use std::{
fmt::Display,
io::{self, Write},
panic::{catch_unwind, resume_unwind},
process::Command,
sync::Arc,
};
use crossterm::{
cursor,
event::{Event, KeyCode, KeyEventKind, KeyModifiers, read},
execute,
terminal::*,
};
use rayon::iter::{ParallelBridge, ParallelIterator};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Version {
Stable(usize),
Beta,
Nightly,
}
impl Version {
fn iterate() -> impl Iterator<Item = Self> {
(0..=95)
.filter(|i| *i != 32)
.map(Self::Stable)
.chain([Self::Beta, Self::Nightly])
}
}
impl Display for Version {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Version::Stable(v) => write!(f, "1.{v}.0"),
Version::Beta => write!(f, "beta"),
Version::Nightly => write!(f, "nightly"),
}
}
}
#[derive(Clone, PartialEq, Eq)]
struct Entry {
output: Arc<Vec<u8>>,
output_noansi: Arc<Vec<u8>>,
version: Version,
}
fn run_rustc(version: Version, args: &[String]) -> io::Result<Entry> {
let mut cmd = Command::new("rustc");
cmd.arg(format!("+{version}"))
.args(["--color", "always"])
.args(args);
let output = cmd.output()?;
Ok(Entry {
output: Arc::new(output.stderr.clone()),
output_noansi: Arc::new(strip_ansi_escapes::strip(output.stderr)),
version,
})
}
fn tui(changes: Vec<Entry>) -> io::Result<()> {
let mut curr = 0;
loop {
{
let mut stdout = io::stdout().lock();
execute!(&mut stdout, Clear(ClearType::All))?;
execute!(&mut stdout, cursor::MoveTo(0, 0))?;
let entry = &changes[curr];
writeln!(&mut stdout, "in version {}\r", entry.version)?;
stdout.write_all(
&entry
.output
.as_ref()
.iter()
.flat_map(|&i| if i == b'\n' { vec![b'\r', i] } else { vec![i] })
.collect::<Vec<_>>(),
)?;
writeln!(&mut stdout)?;
}
match read()? {
Event::Key(event) if let KeyEventKind::Press = event.kind => match event.code {
KeyCode::Esc | KeyCode::Char('q') => break,
KeyCode::Char('c') if event.modifiers.contains(KeyModifiers::CONTROL) => break,
KeyCode::Left => {
curr = curr.saturating_sub(1);
}
KeyCode::Right => {
if curr < changes.len() - 1 {
curr += 1;
}
}
KeyCode::Home => {
curr = 0;
}
KeyCode::End => {
curr = changes.len() - 1;
}
_ => {}
},
_ => {}
}
}
Ok(())
}
fn main() -> io::Result<()> {
let args = std::env::args().skip(1).collect::<Vec<_>>();
let mut outputs = Version::iterate()
.par_bridge()
.flat_map(|version| {
eprintln!("compiling {version}");
let output = match run_rustc(version, &args) {
Ok(i) => i,
Err(e) => {
eprintln!("{e}");
return None;
}
};
Some(output)
})
.collect::<Vec<_>>();
outputs.sort_by_key(|i| i.version);
let mut changes = Vec::new();
let mut last = None::<Entry>;
for curr in outputs {
if let Some(last) = &last
// && last.output_noansi == curr.output_noansi
&& last.output == curr.output
{
continue;
}
last = Some(curr.clone());
changes.push(curr);
}
if changes.is_empty() {
eprintln!("no changes");
return Ok(());
}
enable_raw_mode()?;
let unwind = catch_unwind(move || -> io::Result<()> {
execute!(io::stdout(), EnterAlternateScreen)?;
tui(changes)?;
Ok(())
});
let _ = execute!(io::stdout(), LeaveAlternateScreen);
let _ = disable_raw_mode();
match unwind {
Err(e) => resume_unwind(e),
Ok(r) => r?,
}
Ok(())
}