use super::exit_status::ExitStatus;
use super::interruption::Interruption;
use super::standard_error::StandardError;
use crate::build::{EmittedFormatting, FormattingError};
use crate::emit::EmittedCrate;
pub struct InterruptibleFormatting<'a> {
formatting: EmittedFormatting,
interruption: &'a Interruption,
}
impl<'a> InterruptibleFormatting<'a> {
pub fn new(formatting: EmittedFormatting, interruption: &'a Interruption) -> Self {
Self {
formatting,
interruption,
}
}
pub fn format_or_keep_unformatted(
&self,
emitted: EmittedCrate,
) -> Result<EmittedCrate, ExitStatus> {
match self.formatting.apply(&emitted) {
Ok(formatted) => Ok(formatted),
Err(FormattingError::Interrupted) => Err(ExitStatus::Interrupted),
Err(_) if self.interruption.caused_a_failure() => Err(ExitStatus::Interrupted),
Err(error) => {
StandardError::write_line(format_args!("note: {error}; writing unformatted Rust"));
Ok(emitted)
}
}
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use std::time::Duration;
use super::InterruptibleFormatting;
use crate::build::EmittedFormatting;
use crate::cli::checking::Checking;
use crate::cli::entry_span::EntrySpan;
use crate::cli::expansion::Expansion;
use crate::cli::fake_programs::FakePrograms;
use crate::cli::interruption::Interruption;
use crate::cli::program_file::ProgramFile;
use crate::cli::recorded_output::RecordedOutput;
use crate::cli::source_report::SourceReport;
use crate::cli::stage_work::StageWork;
use crate::emit::{EmittedCrate, RustEmitter};
use crate::source::SourceMap;
struct FormattingFixture {
interruption: Interruption,
}
impl FormattingFixture {
const MODULE: &str = "geometry.knv";
fn new() -> Self {
Self {
interruption: Interruption::default(),
}
}
fn emitted() -> EmittedCrate {
let mut sources = SourceMap::default();
let mut work = StageWork::default();
let output = RecordedOutput::default();
let file = ProgramFile::beside_itself(PathBuf::from(Self::MODULE));
let entry = sources.add(Self::MODULE, FakePrograms::PRINTS_A_SUM);
let program = Expansion::new(&mut sources, &mut work, &output)
.expand(&file, entry)
.expect("the fake program expands");
let checked = Checking::new(SourceReport::new(&sources, &output), &mut work)
.check(&program)
.expect("the fake program checks");
RustEmitter::emit(&checked, EntrySpan::start()).expect("the fake program emits")
}
fn formatting(&self, limit: Duration) -> InterruptibleFormatting<'_> {
InterruptibleFormatting::new(EmittedFormatting::within(limit), &self.interruption)
}
}
#[test]
fn formats_the_emitted_rust() {
let fixture = FormattingFixture::new();
let written = fixture
.formatting(EmittedFormatting::TIME_LIMIT)
.format_or_keep_unformatted(FormattingFixture::emitted());
assert!(written.is_ok_and(|written| written.formatting.is_some()));
}
#[test]
fn keeps_the_rust_unformatted_when_formatting_fails() {
let fixture = FormattingFixture::new();
let written = fixture
.formatting(Duration::ZERO)
.format_or_keep_unformatted(FormattingFixture::emitted());
assert!(written.is_ok_and(|written| written.formatting.is_none()));
}
}