knave 0.14.14

A Lisp programming language for humans and agents that infers types and ownership and compiles to readable Rust
Documentation
use std::io;
use std::path::Path;
use std::rc::Rc;
use std::sync::Arc;

use anstream::AutoStream;

use super::diagnostic_output::JsonDiagnostics;
use super::exit_status::ExitStatus;
use super::finished_program::FinishedProgram;
use super::json_replies::{JsonObject, JsonReplies};
use super::painted_program::PaintedProgram;
use super::streamed_program::StreamedProgram;
use super::terminal::Terminal;
use super::token_colors::TokenColors;
use super::value_marker::ValueMarker;
use crate::diagnostic::Diagnostics;
use crate::expand::{ExpansionSession, ResultPrinter};

pub trait EvaluationOutput {
    fn attach(&self, terminal: &mut Terminal);
    fn printer(&self, session: &mut ExpansionSession<'_>) -> Result<ResultPrinter, Diagnostics>;
    fn run(&self, binary: &Path, printed_forms: Vec<usize>) -> FinishedProgram;
    fn finish(&self, status: ExitStatus);
}

pub enum PrintedEvaluation {
    Plain,
    Painted(ValueMarker),
}

pub struct JsonEvaluation {
    replies: Arc<JsonReplies>,
    marker: ValueMarker,
}

impl PrintedEvaluation {
    const PRINTER: &'static str = "print-form";
    const PRINTER_MODULE: &'static str = "prelude/text";

    fn deciding(colors: TokenColors) -> Self {
        match colors {
            TokenColors::Painted => Self::Painted(ValueMarker::random()),
            TokenColors::Plain => Self::Plain,
        }
    }

    pub fn for_standard_output() -> Self {
        Self::deciding(TokenColors::deciding(AutoStream::choice(&io::stdout())))
    }

    fn plain_printer(session: &mut ExpansionSession<'_>) -> ResultPrinter {
        let printer = session
            .module_value(Self::PRINTER_MODULE, Self::PRINTER)
            .expect("the prelude defines the printer");
        ResultPrinter::calling(printer)
    }
}

impl EvaluationOutput for PrintedEvaluation {
    fn attach(&self, _terminal: &mut Terminal) {}

    fn printer(&self, session: &mut ExpansionSession<'_>) -> Result<ResultPrinter, Diagnostics> {
        match self {
            Self::Plain => Ok(Self::plain_printer(session)),
            Self::Painted(marker) => marker.printer(session),
        }
    }

    fn run(&self, binary: &Path, printed_forms: Vec<usize>) -> FinishedProgram {
        match self {
            Self::Plain => FinishedProgram::run(binary),
            Self::Painted(marker) => {
                PaintedProgram::new(marker.stream(printed_forms)).run(binary, io::stdout().lock())
            }
        }
    }

    fn finish(&self, _status: ExitStatus) {}
}

impl JsonEvaluation {
    const STATUS: &'static str = "status";
    const DONE: &'static str = "done";
    const EXIT: &'static str = "exit";

    pub fn new(id: Option<String>) -> Self {
        Self {
            replies: Arc::new(JsonReplies::new(id)),
            marker: ValueMarker::random(),
        }
    }
}

impl EvaluationOutput for JsonEvaluation {
    fn attach(&self, terminal: &mut Terminal) {
        terminal.report_diagnostics_to(Rc::new(JsonDiagnostics::new(Arc::clone(&self.replies))));
    }

    fn printer(&self, session: &mut ExpansionSession<'_>) -> Result<ResultPrinter, Diagnostics> {
        self.marker.printer(session)
    }

    fn run(&self, binary: &Path, printed_forms: Vec<usize>) -> FinishedProgram {
        let output = self.marker.stream(printed_forms);
        StreamedProgram::new(Arc::clone(&self.replies)).run(binary, output)
    }

    fn finish(&self, status: ExitStatus) {
        if status == ExitStatus::UsageError {
            return;
        }
        let done = JsonObject::new(Self::STATUS, Self::DONE).with(Self::EXIT, status.code());
        let _ = self.replies.send(done);
    }
}