1mod codec;
17pub(crate) mod exec;
18mod parse;
19use std::sync::{
20 atomic::{AtomicBool, Ordering},
21 Arc,
22};
23
24use strop_core::id::ByteOffset;
25use strop_core::Buffer;
26
27pub use exec::STEP_BUDGET;
28use parse::Program;
29
30#[derive(
34 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
35)]
36pub struct SearchMatch {
37 pub start: ByteOffset,
38 pub end: ByteOffset,
39}
40
41impl SearchMatch {
42 pub fn len(&self) -> usize {
43 self.end - self.start
44 }
45 pub fn is_empty(&self) -> bool {
46 self.start == self.end
47 }
48}
49
50#[derive(Debug, Clone, PartialEq, Eq)]
52pub enum QueryError {
53 Unsupported { construct: &'static str, at: usize },
58 UnbalancedGroup { at: usize },
60 UnclosedClass { at: usize },
62 UnclosedOptional { at: usize },
64 BadRepeat { at: usize },
67 BadClass { at: usize },
70 BadCharCode { at: usize },
72 TrailingBackslash { at: usize },
74 TooComplex,
77 Cancelled,
79}
80
81impl std::fmt::Display for QueryError {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 let (head, at): (&str, Option<usize>) = match self {
84 QueryError::Unsupported { construct, at } => {
85 return write!(f, "unsupported query syntax: {construct} (at byte {at})")
86 }
87 QueryError::UnbalancedGroup { at } => ("unbalanced \\( \\)", Some(*at)),
88 QueryError::UnclosedClass { at } => ("unclosed [...]", Some(*at)),
89 QueryError::UnclosedOptional { at } => ("unclosed \\%[", Some(*at)),
90 QueryError::BadRepeat { at } => ("malformed repetition", Some(*at)),
91 QueryError::BadClass { at } => ("malformed [...]", Some(*at)),
92 QueryError::BadCharCode { at } => ("malformed character code", Some(*at)),
93 QueryError::TrailingBackslash { at } => ("trailing backslash", Some(*at)),
94 QueryError::Cancelled => return f.write_str("query cancelled"),
95 QueryError::TooComplex => {
96 return write!(f, "query too complex (over {STEP_BUDGET} steps)")
97 }
98 };
99 match at {
100 Some(at) => write!(f, "{head} (at byte {at})"),
101 None => write!(f, "{head}"),
102 }
103 }
104}
105
106impl std::error::Error for QueryError {}
107
108#[derive(Clone)]
115pub struct CompiledQuery {
116 source: Arc<str>,
117 whole_word: bool,
118 prog: Arc<Program>,
119 cancel: Option<Arc<AtomicBool>>,
120}
121
122impl CompiledQuery {
123 pub fn compile(pattern: &str, whole_word: bool) -> Result<Self, QueryError> {
126 let prog = parse::compile(pattern, whole_word)?;
127 Ok(Self {
128 source: Arc::from(pattern),
129 whole_word,
130 prog: Arc::new(prog),
131 cancel: None,
132 })
133 }
134
135 pub fn cancellable(&self, cancel: Arc<AtomicBool>) -> Self {
138 let mut query = self.clone();
139 query.cancel = Some(cancel);
140 query
141 }
142 pub(crate) fn cancelled(&self) -> bool {
143 self.cancel
144 .as_ref()
145 .is_some_and(|cancel| cancel.load(Ordering::Acquire))
146 }
147
148 pub fn source(&self) -> &str {
150 &self.source
151 }
152
153 pub fn whole_word(&self) -> bool {
156 self.whole_word
157 }
158
159 pub(super) fn program(&self) -> &Program {
160 &self.prog
161 }
162}
163
164impl std::fmt::Debug for CompiledQuery {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 f.debug_struct("CompiledQuery")
167 .field("source", &self.source)
168 .field("whole_word", &self.whole_word)
169 .finish()
170 }
171}
172
173impl PartialEq for CompiledQuery {
174 fn eq(&self, other: &Self) -> bool {
175 self.source == other.source && self.whole_word == other.whole_word
176 }
177}
178impl Eq for CompiledQuery {}
179
180pub fn search_forward(
183 buf: &Buffer,
184 from: usize,
185 query: &CompiledQuery,
186) -> Result<Option<SearchMatch>, QueryError> {
187 super::resolve::search::forward(buf, from, query)
188}
189
190pub fn search_backward(
193 buf: &Buffer,
194 from: usize,
195 query: &CompiledQuery,
196) -> Result<Option<SearchMatch>, QueryError> {
197 super::resolve::search::backward(buf, from, query)
198}
199
200pub fn search_all(buf: &Buffer, query: &CompiledQuery) -> Result<Vec<SearchMatch>, QueryError> {
204 super::resolve::search::all(buf, query)
205}
206
207pub fn search_visit(
211 buf: &Buffer,
212 query: &CompiledQuery,
213 visitor: impl FnMut(SearchMatch) -> std::ops::ControlFlow<()>,
214) -> Result<(), QueryError> {
215 super::resolve::search::visit_all(buf, query, visitor)
216}