use std::{cell::Cell, marker, sync::Arc};
use cargo_util::ProcessBuilder;
use crate::compiler::future_incompat::FutureBreakageItem;
use crate::compiler::locking::LockKey;
use crate::compiler::timings::SectionTiming;
use crate::context::WarningHandling;
use crate::util::Queue;
use crate::util::interning::InternedString;
use crate::{CargoResult, compiler::locking::LockManager};
use super::{Artifact, DiagDedupe, Job, JobId, Message};
pub struct JobState<'a, 'gctx> {
messages: Arc<Queue<Message>>,
output: Option<&'a DiagDedupe<'gctx>>,
id: JobId,
rmeta_required: Cell<bool>,
lock_manager: Arc<LockManager>,
warning_handling: WarningHandling,
_marker: marker::PhantomData<&'a ()>,
}
impl<'a, 'gctx> JobState<'a, 'gctx> {
pub(super) fn new(
id: JobId,
messages: Arc<Queue<Message>>,
output: Option<&'a DiagDedupe<'gctx>>,
rmeta_required: bool,
lock_manager: Arc<LockManager>,
warning_handling: WarningHandling,
) -> Self {
Self {
id,
messages,
output,
rmeta_required: Cell::new(rmeta_required),
lock_manager,
warning_handling,
_marker: marker::PhantomData,
}
}
pub fn running(&self, cmd: &ProcessBuilder) {
self.messages.push(Message::Run(self.id, cmd.to_string()));
}
pub fn stdout(&self, stdout: String) -> CargoResult<()> {
if let Some(dedupe) = self.output {
writeln!(dedupe.gctx.shell().out(), "{}", stdout)?;
} else {
self.messages.push_bounded(Message::Stdout(stdout));
}
Ok(())
}
pub fn stderr(&self, stderr: String) -> CargoResult<()> {
if let Some(dedupe) = self.output {
let mut shell = dedupe.gctx.shell();
shell.print_ansi_stderr(stderr.as_bytes())?;
shell.err().write_all(b"\n")?;
} else {
self.messages.push_bounded(Message::Stderr(stderr));
}
Ok(())
}
pub fn emit_diag(
&self,
level: &str,
diag: String,
lint: bool,
fixable: bool,
) -> CargoResult<()> {
if level == "warning" && lint && self.warning_handling == WarningHandling::Allow {
tracing::warn!("{diag}");
} else if let Some(dedupe) = self.output {
let emitted = dedupe.emit_diag(&diag)?;
if level == "warning" {
self.messages.push(Message::WarningCount {
id: self.id,
lint,
emitted,
fixable,
});
}
} else {
self.messages.push_bounded(Message::Diagnostic {
id: self.id,
level: level.to_string(),
diag,
lint,
fixable,
});
}
Ok(())
}
pub fn warning(&self, warning: String) {
self.messages.push_bounded(Message::Warning {
id: self.id,
warning,
});
}
pub fn rmeta_produced(&self) {
self.rmeta_required.set(false);
self.messages
.push(Message::Finish(self.id, Artifact::Metadata, Ok(())));
}
pub fn lock_exclusive(&self, lock: &LockKey) -> CargoResult<()> {
self.lock_manager.lock(lock)
}
pub fn downgrade_to_shared(&self, lock: &LockKey) -> CargoResult<()> {
self.lock_manager.downgrade_to_shared(lock)
}
pub fn on_section_timing_emitted(&self, section: SectionTiming) {
self.messages.push(Message::SectionTiming(self.id, section));
}
pub(super) fn run_to_finish(self, job: Job) {
let mut sender = FinishOnDrop {
messages: &self.messages,
id: self.id,
result: None,
};
sender.result = Some(job.run(&self));
if self.rmeta_required.get() && sender.result.as_ref().unwrap().is_ok() {
self.messages
.push(Message::Finish(self.id, Artifact::Metadata, Ok(())));
}
struct FinishOnDrop<'a> {
messages: &'a Queue<Message>,
id: JobId,
result: Option<CargoResult<()>>,
}
impl Drop for FinishOnDrop<'_> {
fn drop(&mut self) {
let result = self
.result
.take()
.unwrap_or_else(|| Err(anyhow::format_err!("worker panicked")));
self.messages
.push(Message::Finish(self.id, Artifact::All, result));
}
}
}
pub fn future_incompat_report(&self, report: Vec<FutureBreakageItem>) {
self.messages
.push(Message::FutureIncompatReport(self.id, report));
}
pub fn unused_externs(&self, unused_externs: std::collections::BTreeSet<InternedString>) {
self.messages
.push(Message::UnusedExterns(self.id, unused_externs));
}
}