cargo hollywood
Diffstat (limited to 'src/compiler/mod.rs')
| -rw-r--r-- | src/compiler/mod.rs | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/src/compiler/mod.rs b/src/compiler/mod.rs new file mode 100644 index 0000000..b06f8e2 --- /dev/null +++ b/src/compiler/mod.rs @@ -0,0 +1,189 @@ +//! compiler output ui +use anyhow::Result; +use cargo_metadata::{diagnostic::Diagnostic, CompilerMessage, Message, PackageId}; +use crossbeam::channel::Receiver; +use crossterm::event::{self, Event, KeyCode}; +use ratatui::prelude::*; +use std::{ + ops::ControlFlow, + time::{Duration, Instant}, +}; + +use crate::{ + cargo::{self, TestMessage}, + ui::SList, +}; + +mod ui; + +const BUILT_SCRIPT: u8 = 1; +const BUILD_SCRIPT_EXECUTED: u8 = 2; +const FINISHED: u8 = 4; + +struct Crate { + pid: PackageId, + /// bitflag, see above + state: u8, +} + +struct State { + compiled: SList, + crates: Vec<Crate>, + diagnostics: Vec<String>, + rx: Receiver<TestMessage>, + failed: bool, +} + +impl State { + fn new(rx: Receiver<TestMessage>) -> Self { + Self { + compiled: SList::default(), + diagnostics: vec![], + crates: vec![], + failed: false, + rx, + } + } + + fn recv(&mut self) -> RecvStatus { + let deadline = Instant::now() + Duration::from_millis(50); + while let Ok(event) = self.rx.recv_deadline(deadline) { + match event { + TestMessage::CompilerEvent(e) => match e { + Message::BuildFinished(b) => { + return match b.success { + true => RecvStatus::Finished, + false => RecvStatus::Failed, + } + } + Message::BuildScriptExecuted(f) => { + let p = self + .crates + .iter() + .position(|Crate { pid, .. }| pid == &f.package_id) + .unwrap(); + self.crates[p].state |= BUILD_SCRIPT_EXECUTED; + } + Message::CompilerArtifact(c) => { + self.compiled.itemc += 1; + if c.target.name == "build-script-build" { + self.crates.push(Crate { + pid: c.package_id, + state: BUILT_SCRIPT, + }); + } else { + match self + .crates + .iter() + .position(|Crate { pid, .. }| pid == &c.package_id) + { + None => self.crates.push(Crate { + pid: c.package_id, + state: FINISHED, + }), + Some(n) => self.crates[n].state |= FINISHED, + } + } + } + Message::CompilerMessage(CompilerMessage { + message: + Diagnostic { + rendered: Some(rendered), + .. + }, + .. + }) => { + if self.diagnostics.contains(&rendered) { + continue; + }; + self.diagnostics.push(rendered); + } + // Message::CompilerMessage(CompilerMessage { message, .. }) => { + // let mut h = ahash::AHasher::default(); + // message.hash(&mut h); + // let v = h.finish(); + // log::trace!("got {message}"); + // if self.diagnostics.iter().all(|&(_, hash)| (hash != v)) { + // if let Some(span) = message.spans.first() { + // let f = std::fs::read_to_string(at.join(span.file_name.clone())) + // .unwrap(); + // let mut e = lerr::Error::new(&f); + // e.message(format!( + // "{}: {}", + // match message.level { + // DiagnosticLevel::Help => + // cformat_args!("{green}help{reset}"), + // DiagnosticLevel::Note => cformat_args!("{cyan}note{reset}"), + // DiagnosticLevel::Warning => + // cformat_args!("{yellow}nit{reset}"), + // _ => cformat_args!("{red}error{reset}"), + // }, + // message.message + // )); + // for span in message.spans { + // e.label(( + // span.byte_start as usize..span.byte_end as usize, + // span.label.unwrap_or("here".to_string()), + // )); + // } + // self.diagnostics.push((e.to_string(), v)); + // continue; + // } else { + // let mut e = lerr::Error::new("\n"); + // e.message(format!( + // "{}: {}", + // match message.level { + // DiagnosticLevel::Help => + // cformat_args!("{green}help{reset}"), + // DiagnosticLevel::Note => cformat_args!("{cyan}note{reset}"), + // _ => cformat_args!("{red}error{reset}"), + // }, + // message.message + // )); + // self.diagnostics.push((e.to_string(), v)); + // continue; + // } + // } + _ => {} + }, + e => unreachable!("got bad event {e:?}"), + } + } + RecvStatus::None + } +} + +enum RecvStatus { + Finished, + Failed, + None, +} + +pub fn run<B: Backend>( + terminal: &mut Terminal<B>, + meta: &cargo::Metadata, + rx: Receiver<TestMessage>, +) -> Result<ControlFlow<(), Receiver<TestMessage>>> { + let mut state = State::new(rx); + loop { + terminal.draw(|f| ui::ui(f, &mut state, meta))?; + if event::poll(Duration::from_millis(5))? { + if let Event::Key(key) = event::read()? { + match key.code { + KeyCode::Char('q') => return Ok(ControlFlow::Break(())), + KeyCode::Down | KeyCode::Char('s') => state.compiled.next(), + KeyCode::Up | KeyCode::Char('w') => state.compiled.prev(), + _ => {} + } + } + } + if state.failed { + continue; + } + match state.recv() { + RecvStatus::Failed => state.failed = true, + RecvStatus::None => {} + RecvStatus::Finished => return Ok(ControlFlow::Continue(state.rx)), + }; + } +} |