1use anyhow::{Result, anyhow, ensure};
2use serde::Serialize;
3
4use crate::formats::eite::util::{
5 array::{print_arr, set_element, str_print_arr},
6 string::{
7 int_arr_from_str_printed_arr, str_join_esc_no_trailing,
8 str_split_escaped,
9 },
10};
11
12#[derive(Default, Serialize)]
16pub struct EiteState {
17 pub debug_level: i32,
19 pub env_preferred_format: String,
20 pub env_char_encoding: String,
21 pub env_terminal_type: String,
22 pub env_language: String,
23 pub env_locale_config: String,
24 pub env_code_language: String,
25 pub env_resolution_w: i32,
26 pub env_resolution_h: i32,
27
28 pub document_exec_data: Vec<String>,
30 pub document_exec_symbol_index: Vec<String>,
31 pub document_exec_ptrs: Vec<String>,
32 pub document_exec_frames: Vec<String>,
33 pub document_exec_events: Vec<String>,
34 pub document_exec_logs: Vec<String>,
35 pub document_exec_settings: Vec<String>,
36
37 pub tests_passed: u32,
39 pub tests_failed: u32,
40 pub tests_total: u32,
41
42 pub import_settings: Vec<String>,
44 pub export_settings: Vec<String>,
45 pub import_deferred_settings_stack: Vec<String>,
46 pub export_deferred_settings_stack: Vec<String>,
47
48 pub storage_cfg: Vec<String>,
50}
51
52impl EiteState {
53 pub fn new() -> Self {
54 Self {
55 debug_level: 0,
56 env_preferred_format: String::new(),
57 env_char_encoding: "asciiSafeSubset".into(),
58 env_terminal_type: "vt100".into(),
59 env_language: "en-US".into(),
60 env_locale_config: "inherit:usa,".into(),
61 env_code_language: "javascript".into(),
62 env_resolution_w: 0,
63 env_resolution_h: 0,
64 ..Default::default()
65 }
66 }
67
68 pub fn prepare_document_exec(&mut self, contents: &[u32]) -> usize {
71 let exec_id = self.document_exec_ptrs.len();
72 self.document_exec_data.push(str_print_arr(contents));
73 self.document_exec_symbol_index.push(String::new());
74 self.document_exec_ptrs.push("0,".to_string());
76 self.document_exec_frames.push(String::new());
77 self.document_exec_events.push(String::new());
78 self.document_exec_logs.push(String::new());
79 self.document_exec_settings.push(",".to_string()); exec_id
81 }
82
83 pub fn is_exec_id(&self, exec_id: usize) -> bool {
84 exec_id < self.document_exec_ptrs.len()
85 }
86
87 pub fn get_exec_settings(&self, exec_id: usize) -> Result<Vec<String>> {
89 if !self.is_exec_id(exec_id) {
90 return Err(anyhow!("Exec id out of range"));
91 }
92 let settings = self
93 .document_exec_settings
94 .get(exec_id)
95 .ok_or_else(|| anyhow!("Exec id not found"))?;
96 Ok(settings
97 .split(',')
98 .filter(|s| !s.is_empty())
99 .map(std::string::ToString::to_string)
100 .collect())
101 }
102
103 pub fn set_exec_settings(
105 &mut self,
106 exec_id: usize,
107 kv: &[String],
108 ) -> Result<()> {
109 if !self.is_exec_id(exec_id) {
110 return Err(anyhow!("Exec id out of range"));
111 }
112 let mut joined = String::new();
113 for k in kv {
114 joined.push_str(k);
115 joined.push(',');
116 }
117 self.document_exec_settings[exec_id] = joined;
118 Ok(())
119 }
120
121 pub fn get_exec_ptrs(&self, exec_id: usize) -> Result<Vec<String>> {
122 if !self.is_exec_id(exec_id) {
123 return Err(anyhow!("Exec id out of range"));
124 }
125 Ok(str_split_escaped(&self.document_exec_ptrs[exec_id], ","))
126 }
127
128 pub fn set_exec_ptrs(
129 &mut self,
130 exec_id: usize,
131 ptrs: &[String],
132 ) -> Result<()> {
133 if !self.is_exec_id(exec_id) {
134 return Err(anyhow!("Exec id out of range"));
135 }
136 self.document_exec_ptrs[exec_id] = str_join_esc_no_trailing(ptrs, ",");
137 Ok(())
138 }
139
140 fn parse_ptr(val: &str) -> u32 {
141 if val.is_empty() {
142 0
143 } else {
144 val.parse::<u32>().unwrap_or(0)
145 }
146 }
147
148 pub fn get_current_exec_ptr_pos(&self, exec_id: usize) -> Result<u32> {
149 let ptrs = self.get_exec_ptrs(exec_id)?;
150 if let Some(last) = ptrs.last() {
151 Ok(Self::parse_ptr(last))
152 } else {
153 Ok(0)
154 }
155 }
156
157 pub fn set_exec_ptr_pos(
158 &mut self,
159 exec_id: usize,
160 new_pos: u32,
161 ) -> Result<()> {
162 let mut ptrs = self.get_exec_ptrs(exec_id)?;
163 set_element(&mut ptrs, -1, new_pos.to_string());
164 self.set_exec_ptrs(exec_id, &ptrs)?;
165 Ok(())
166 }
167
168 pub fn incr_exec_ptr_pos(&mut self, exec_id: usize) -> Result<()> {
169 let cur = self.get_current_exec_ptr_pos(exec_id)?;
170 self.set_exec_ptr_pos(exec_id, cur + 1)
171 }
172
173 pub fn get_next_level_exec_ptr_pos(&self, exec_id: usize) -> Result<u32> {
174 let ptrs = self.get_exec_ptrs(exec_id)?;
175 if ptrs.len() >= 2 {
176 Ok(Self::parse_ptr(&ptrs[ptrs.len() - 2]))
177 } else {
178 Ok(0)
179 }
180 }
181
182 pub fn get_current_exec_data(&self, exec_id: usize) -> Result<Vec<u32>> {
183 if !self.is_exec_id(exec_id) {
184 return Err(anyhow!("Exec id out of range"));
185 }
186 int_arr_from_str_printed_arr(&self.document_exec_data[exec_id])
187 }
188
189 pub fn get_current_exec_frame(&self, exec_id: usize) -> Result<Vec<u32>> {
190 if !self.is_exec_id(exec_id) {
191 return Err(anyhow!("Exec id out of range"));
192 }
193 int_arr_from_str_printed_arr(&self.document_exec_frames[exec_id])
194 }
195
196 pub fn get_exec_option(
198 &self,
199 exec_id: usize,
200 key: &str,
201 ) -> Result<Option<String>> {
202 let kv = self.get_exec_settings(exec_id)?;
203 for pair in kv {
204 if let Some((k, v)) = pair.split_once('=') {
205 if k == key {
206 return Ok(Some(v.to_string()));
207 }
208 }
209 }
210 Ok(None)
211 }
212
213 pub fn set_exec_option(
215 &mut self,
216 exec_id: usize,
217 key: &str,
218 val: &str,
219 ) -> Result<()> {
220 let mut kv = self.get_exec_settings(exec_id)?;
221 let mut found = false;
222 for item in &mut kv {
223 if let Some((k, _)) = item.split_once('=') {
224 if k == key {
225 *item = format!("{key}={val}");
226 found = true;
227 break;
228 }
229 }
230 }
231 if !found {
232 kv.push(format!("{key}={val}"));
233 }
234 self.set_exec_settings(exec_id, &kv)
235 }
236
237 pub fn set_exec_frame(
239 &mut self,
240 exec_id: usize,
241 frame: &[u32],
242 ) -> Result<()> {
243 ensure!(self.is_exec_id(exec_id), "Invalid exec id {exec_id}");
244 if exec_id >= self.document_exec_frames.len() {
245 self.document_exec_frames.resize(exec_id + 1, String::new());
246 }
247 self.document_exec_frames[exec_id] = print_arr(frame);
248 Ok(())
249 }
250
251 pub fn get_env_preferred_format(&self) -> &str {
252 if self.env_preferred_format.is_empty() {
253 "utf8"
254 } else {
255 &self.env_preferred_format
256 }
257 }
258}