1use std::path::Path;
2
3use pfx_editor_shell::{Script, drive};
4
5use crate::editor::{Editor, Layout};
6
7pub fn place(text: &str, layout: &Layout) -> Result<String, String> {
8 let mut lines = Vec::new();
9 for (index, line) in text.lines().enumerate() {
10 if line.trim_start().starts_with('#') {
11 lines.push(line.to_string());
12 continue;
13 }
14 let mut words = Vec::new();
15 for word in line.split_whitespace() {
16 match word.strip_prefix('@') {
17 Some(name) => {
18 let [x, y] = layout.find(name).ok_or_else(|| {
19 format!("line {}: nothing on screen is named {name}", index + 1)
20 })?;
21 words.push(format!("{x} {y}"));
22 }
23 None => words.push(word.to_string()),
24 }
25 }
26 lines.push(words.join(" "));
27 }
28 Ok(lines.join("\n"))
29}
30
31pub fn script(text: &str, layout: &Layout) -> Result<Script, String> {
32 Script::parse(&place(text, layout)?).map_err(|error| error.to_string())
33}
34
35pub fn resolve(path: &Path, size: [u32; 2], text: &str) -> Result<Script, String> {
36 let mut done: Vec<String> = Vec::new();
37 for line in text.lines() {
38 if !line.contains('@') || line.trim_start().starts_with('#') {
39 done.push(line.to_string());
40 continue;
41 }
42 let mut editor = Editor::open(path)?;
43 let ctx = egui::Context::default();
44 pfx_editor_style::theme::apply_theme(&ctx, &crate::theme::pfx());
45 let before = Script::parse(&done.join("\n")).map_err(|error| error.to_string())?;
46 drive(&ctx, size, &before, |ctx| editor.ui(ctx));
47 done.push(place(line, &editor.layout)?);
48 }
49 Script::parse(&done.join("\n")).map_err(|error| error.to_string())
50}
51
52pub fn frames(count: usize) -> String {
53 "frame\n".repeat(count)
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
61 fn named_points_become_coordinates_and_an_unknown_name_names_its_line() {
62 let mut layout = Layout::default();
63 layout.buttons.insert(
64 "move",
65 egui::Rect::from_min_size(egui::pos2(10.0, 20.0), egui::vec2(20.0, 10.0)),
66 );
67 layout.viewport = Some(egui::Rect::from_min_size(
68 egui::pos2(100.0, 0.0),
69 egui::vec2(200.0, 100.0),
70 ));
71 assert_eq!(
72 place(
73 "# @button:move stays a comment\nclick @button:move\ndrag @viewport @viewport:0.1,0.9 middle\nkey ctrl+Z",
74 &layout
75 )
76 .unwrap(),
77 "# @button:move stays a comment\nclick 20 25\ndrag 200 50 120 90 middle\nkey ctrl+Z"
78 );
79 let error = place("frame\nclick @button:gone", &layout).unwrap_err();
80 assert!(error.starts_with("line 2:"), "{error}");
81 assert_eq!(
82 script("click @button:move", &layout).unwrap().steps.len(),
83 1
84 );
85 assert!(script("wiggle 3", &layout).is_err());
86 assert_eq!(frames(2), "frame\nframe\n");
87 }
88}