Skip to main content

yggdrasil/components/code_runner/runner/
execution.rs

1//! 一次运行的共享状态与「命令」信号。
2//!
3//! 外壳(调用方所在包)持有状态信号并发出命令;激活后加载的分包函数
4//! `code_runner_body` 负责消费命令:启动流、写终端。JS 句柄只存在于分包内,
5//! 外壳与分包之间只经由这些 `Send + Sync` 的信号通信。
6
7use super::{
8    state::{Outcome, Phase, RunState},
9    RunnerExample,
10};
11use crate::api::code_runner::ExecRequest;
12use leptos::prelude::*;
13
14/// 外壳发给终端的命令;终端未挂载时直接丢弃(挂载时按 `example_output` 自行写入)。
15#[cfg_attr(not(target_arch = "wasm32"), allow(dead_code))]
16#[derive(Clone, Debug, PartialEq)]
17pub(super) enum TerminalCommand {
18    Clear,
19    WriteAll { stdout: String, stderr: String },
20}
21
22#[derive(Clone, Copy)]
23pub(super) struct Execution {
24    pub state: RwSignal<RunState>,
25    pub show_output: RwSignal<bool>,
26    pub has_output: RwSignal<bool>,
27    /// 待提交的运行请求,由分包里的执行 Effect 取走(终端就绪后才提交)。
28    pub pending: RwSignal<Option<ExecRequest>>,
29    /// 待执行的终端命令,由分包里的终端 Effect 取走。
30    pub terminal_command: RwSignal<Option<TerminalCommand>>,
31}
32
33impl Execution {
34    pub fn new(example: Option<&RunnerExample>) -> Self {
35        Self {
36            state: RwSignal::new(example.map_or(RunState::Idle, example_state)),
37            show_output: RwSignal::new(example.is_some()),
38            has_output: RwSignal::new(example.is_some()),
39            pending: RwSignal::new(None),
40            terminal_command: RwSignal::new(None),
41        }
42    }
43
44    pub fn show_example(self, example: &RunnerExample) {
45        self.show_output.set(true);
46        self.has_output.set(true);
47        self.state.set(example_state(example));
48        self.terminal_command.set(Some(TerminalCommand::WriteAll {
49            stdout: example.stdout.clone(),
50            stderr: example.stderr.clone(),
51        }));
52    }
53
54    pub fn clear(self) {
55        self.terminal_command.set(Some(TerminalCommand::Clear));
56        self.state.set(RunState::Idle);
57        self.has_output.set(false);
58    }
59
60    pub fn start(self, request: ExecRequest) {
61        if self.state.get_untracked().is_running() {
62            return;
63        }
64        self.clear();
65        self.state.set(RunState::Busy(Phase::Preparing));
66        self.show_output.set(true);
67        self.pending.set(Some(request));
68    }
69
70    /// 当前是否在运行(非响应式;组件已卸载时视为未运行)。
71    #[cfg_attr(not(target_arch = "wasm32"), allow(dead_code))]
72    pub fn is_running_untracked(self) -> bool {
73        self.state
74            .try_with_untracked(RunState::is_running)
75            .unwrap_or(false)
76    }
77}
78
79fn example_state(example: &RunnerExample) -> RunState {
80    RunState::Finished(Outcome::Example {
81        meta: example.status.clone(),
82        error: example.error.clone(),
83    })
84}