logicaffeine_web/ui/pages/
studio_actions.rs1use crate::ui::state::StudioMode;
10
11#[derive(Clone, Debug, PartialEq, Eq)]
13pub enum Offer {
14 Ready,
15 Unavailable(String),
16}
17
18impl Offer {
19 pub fn is_ready(&self) -> bool {
20 matches!(self, Offer::Ready)
21 }
22
23 pub fn reason(&self) -> Option<&str> {
25 match self {
26 Offer::Ready => None,
27 Offer::Unavailable(why) => Some(why),
28 }
29 }
30}
31
32#[derive(Clone, Debug, PartialEq, Eq)]
34pub struct Actions {
35 pub run: Offer,
37 pub compile: Offer,
38 pub debug: Offer,
40}
41
42pub fn actions_for(mode: StudioMode, path: Option<&str>, content: &str) -> Actions {
45 let unavailable = |why: &str| Offer::Unavailable(why.to_string());
46 if content.trim().is_empty() {
47 let empty = || unavailable("The editor is empty. Write something first.");
48 return Actions { run: empty(), compile: empty(), debug: empty() };
49 }
50 let only_code = || unavailable("The debugger steps through Code programs.");
51 match mode {
52 StudioMode::Code => {
53 let run = match path.and_then(crate::ui::examples::compiled_runtime_notice) {
54 Some(notice) => Offer::Unavailable(notice),
55 None => Offer::Ready,
56 };
57 Actions { debug: run.clone(), run, compile: Offer::Ready }
58 }
59 StudioMode::Logic => Actions {
60 run: Offer::Ready,
61 compile: match logicaffeine_compile::logic_compile_note(content) {
62 Some(note) => unavailable(note),
63 None => Offer::Ready,
64 },
65 debug: only_code(),
66 },
67 StudioMode::Math => Actions {
68 run: Offer::Ready,
69 compile: if is_theory(content) {
70 unavailable("A theory is certified by Execute. Compile turns Definitions and Inductives into Rust, and a theory declares none.")
71 } else if !declares_anything(content) {
72 unavailable("Compile turns Definitions and Inductives into Rust, and this file declares none. Execute runs its Checks and Evals.")
73 } else {
74 Offer::Ready
75 },
76 debug: only_code(),
77 },
78 StudioMode::Hardware => Actions {
79 run: Offer::Ready,
80 compile: match crate::ui::pages::hardware_report::kind_of(content)
81 .and_then(crate::ui::pages::hardware_report::nothing_to_compile)
82 {
83 Some(note) => unavailable(note),
84 None => Offer::Ready,
85 },
86 debug: only_code(),
87 },
88 StudioMode::Web => {
89 let live = || unavailable("The preview runs the app as you type.");
90 Actions { run: live(), compile: live(), debug: live() }
91 }
92 }
93}
94
95fn is_theory(content: &str) -> bool {
97 content.lines().any(|line| line.trim_start().starts_with("## Theory"))
98}
99
100fn declares_anything(content: &str) -> bool {
103 crate::ui::pages::studio::parse_math_statements(content).iter().any(|statement| {
104 let s = statement.trim_start();
105 s.starts_with("Definition")
106 || s.starts_with("Inductive")
107 || s.starts_with("## To ")
108 || s.starts_with("## Theorem:")
109 || (s.starts_with("Let ") && s.contains(" be "))
110 || ((s.starts_with("A ") || s.starts_with("An ")) && s.contains(" is either"))
111 })
112}
113
114#[cfg(test)]
115#[path = "../../../../../crates/logicaffeine_tests/unit/logicaffeine_web/ui/pages/studio_actions/tests.rs"]
116mod tests;