knave 0.14.22

A Lisp programming language for humans and agents that infers types and ownership and compiles to readable Rust
Documentation
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()));
    }
}