Repository: typst/typst
File: crates/typst-cli/src/eval.rs (Rust)

Code before:
            for (i, error) in errors.into_iter().enumerate() {
                message.push_str(if i == 0 { ": " } else { ", " });
                message.push_str(&error.message);
            }
            message
        })?;

        scope.bind(key.into(), Binding::detached(value));
    }

    Ok(scope)
}

/// Evaluates the statement with the given mode and scope.
fn evaluate_statement(
    statement: String,
    mode: SyntaxMode,
    scope: Scope,
    world: &dyn World,
    introspector: &Introspector,
) -> SourceResult<Value> {
    eval_string(
        &typst::ROUTINES,
        world.track(),
        // TODO: propagate warnings
        Sink::new().track_mut(),
        introspector.track(),
        Context::new(None, Some(StyleChain::new(&world.library().styles))).track(),
        &statement,
        Span::detached(),
        match mode {
            SyntaxMode::Code => typst::syntax::SyntaxMode::Code,
            SyntaxMode::Markup => typst::syntax::SyntaxMode::Markup,
            SyntaxMode::Math => typst::syntax::SyntaxMode::Math,
        },
        scope,
    )
}

/// Reads a statement from stdin, decoding it from UTF-8.
fn read_statement_from_stdin() -> HintedStrResult<String> {
    let result = read_from_stdin()?;
    let statement = decode_utf8(&result)?;
    Ok(statement.into())
}


Review comment (suggestion):
Retaining the warnings here and printing them should be _fairly_ straightforward I think and might be good to have

Code after:
use comemo::Track;
use ecow::eco_format;
use typst::diag::{HintedStrResult, SourceResult, Warned};
use typst::foundations::{Context, Scope, StyleChain, Value};
use typst::syntax::{Span, SyntaxMode};
use typst::{World, engine::Sink, introspection::Introspector, layout::PagedDocument};
use typst_eval::eval_string;
use typst_html::HtmlDocument;

use crate::args::{EvalCommand, Target};
use crate::compile::print_diagnostics;
use crate::set_failed;
use crate::world::SystemWorld;

/// Execute a query command.
pub fn eval(command: &'static EvalCommand) -> HintedStrResult<()> {
    let mut world =
        SystemWorld::new(command.r#in.as_ref(), &command.world, &command.process)?;

    // Reset everything and ensure that the main file is present.
    world.reset();
    world.source(world.main()).map_err(|err| err.to_string())?;

    // Compile the main file and get the introspector.
    let Warned { output, mut warnings } = match command.target {
        Target::Paged => typst::compile::<PagedDocument>(&world)
            .map(|output| output.map(|document| document.introspector)),
        Target::Html => typst::compile::<HtmlDocument>(&world)
            .map(|output| output.map(|document| document.introspector)),
    };

    match output {
        // Retrieve and print evaluation results.
        Ok(introspector) => {
            let mut sink = Sink::new();
            let eval_result = evaluate_expression(
                command.expression.clone(),
                &mut sink,
                &world,
                &introspector,
            );
            let errors = match &eval_result {
                Err(errors) => errors.as_slice(),
                Ok(value) => {
                    let serialized =
                        crate::serialize(value, command.format, command.pretty)?;
                    println!("{serialized}");
                    &[]
                }
            };
            // Collect additional warnings from evaluating the expression.
            warnings.extend(sink.warnings());

            print_diagnostics(
                &world,
                errors,
                &warnings,
                command.process.diagnostic_format,
            )
            .map_err(|err| eco_format!("failed to print diagnostics ({err})"))?;
        }

        // Print diagnostics.
        Err(errors) => {
            set_failed();
            print_diagnostics(
                &world,
                &errors,
                &warnings,
                command.process.diagnostic_format,
            )
            .map_err(|err| eco_format!("failed to print diagnostics ({err})"))?;
        }
    }

    Ok(())
}

/// Evaluates the expression with code syntax mode and no scope.
fn evaluate_expression(
    expression: String,
    sink: &mut Sink,
    world: &dyn World,
    introspector: &Introspector,
) -> SourceResult<Value> {
    eval_string(
        &typst::ROUTINES,
        world.track(),
        sink.track_mut(),
        introspector.track(),
        Context::new(None, Some(StyleChain::new(&world.library().styles))).track(),
        &expression,
        Span::detached(),
        SyntaxMode::Code,
        Scope::default(),
    )
}