Skip to main content

rgx/
app.rs

1use crate::engine::{self, CompiledRegex, EngineFlags, EngineKind, RegexEngine};
2use crate::explain::{self, ExplainNode};
3use crate::input::editor::Editor;
4
5pub struct App {
6    pub regex_editor: Editor,
7    pub test_editor: Editor,
8    pub focused_panel: u8, // 0 = regex, 1 = test string
9    pub engine_kind: EngineKind,
10    pub flags: EngineFlags,
11    pub matches: Vec<engine::Match>,
12    pub explanation: Vec<ExplainNode>,
13    pub error: Option<String>,
14    pub show_help: bool,
15    pub should_quit: bool,
16    engine: Box<dyn RegexEngine>,
17    compiled: Option<Box<dyn CompiledRegex>>,
18}
19
20impl App {
21    pub fn new(engine_kind: EngineKind, flags: EngineFlags) -> Self {
22        let engine = engine::create_engine(engine_kind);
23        Self {
24            regex_editor: Editor::new(),
25            test_editor: Editor::new(),
26            focused_panel: 0,
27            engine_kind,
28            flags,
29            matches: Vec::new(),
30            explanation: Vec::new(),
31            error: None,
32            show_help: false,
33            should_quit: false,
34            engine,
35            compiled: None,
36        }
37    }
38
39    pub fn set_pattern(&mut self, pattern: &str) {
40        self.regex_editor = Editor::with_content(pattern.to_string());
41        self.recompute();
42    }
43
44    pub fn set_test_string(&mut self, text: &str) {
45        self.test_editor = Editor::with_content(text.to_string());
46        self.rematch();
47    }
48
49    pub fn switch_engine(&mut self) {
50        self.engine_kind = self.engine_kind.next();
51        self.engine = engine::create_engine(self.engine_kind);
52        self.recompute();
53    }
54
55    pub fn recompute(&mut self) {
56        let pattern = self.regex_editor.content().to_string();
57
58        if pattern.is_empty() {
59            self.compiled = None;
60            self.matches.clear();
61            self.explanation.clear();
62            self.error = None;
63            return;
64        }
65
66        // Compile
67        match self.engine.compile(&pattern, &self.flags) {
68            Ok(compiled) => {
69                self.compiled = Some(compiled);
70                self.error = None;
71            }
72            Err(e) => {
73                self.compiled = None;
74                self.matches.clear();
75                self.error = Some(e.to_string());
76            }
77        }
78
79        // Explain (uses regex-syntax, independent of engine)
80        match explain::explain(&pattern) {
81            Ok(nodes) => self.explanation = nodes,
82            Err(e) => {
83                self.explanation.clear();
84                if self.error.is_none() {
85                    self.error = Some(e);
86                }
87            }
88        }
89
90        // Match
91        self.rematch();
92    }
93
94    pub fn rematch(&mut self) {
95        if let Some(compiled) = &self.compiled {
96            let text = self.test_editor.content().to_string();
97            if text.is_empty() {
98                self.matches.clear();
99                return;
100            }
101            match compiled.find_matches(&text) {
102                Ok(m) => self.matches = m,
103                Err(e) => {
104                    self.matches.clear();
105                    self.error = Some(e.to_string());
106                }
107            }
108        } else {
109            self.matches.clear();
110        }
111    }
112}