hyper_scripter/query/
mod.rs1use crate::error::{
2 Contextable, DisplayError, DisplayResult,
3 FormatCode::{Regex as RegexCode, ScriptQuery as ScriptQueryCode},
4 Result,
5};
6use crate::script::{ConcreteScriptName, IntoScriptName, ScriptName};
7use crate::util::impl_ser_by_to_string;
8use regex::Regex;
9use std::num::NonZeroUsize;
10use std::str::FromStr;
11
12mod util;
13pub use util::*;
14mod range_query;
15pub use range_query::*;
16mod list_query_handler;
17mod the_multifuzz_algo;
18pub use list_query_handler::*;
19
20#[derive(Debug, Eq, PartialEq, Display)]
21pub enum EditQuery<Q> {
22 #[display(fmt = "?")]
23 NewAnonimous,
24 #[display(fmt = "{}", _0)]
25 Query(Q),
26}
27impl<Q: FromStr<Err = DisplayError>> FromStr for EditQuery<Q> {
28 type Err = DisplayError;
29 fn from_str(s: &str) -> DisplayResult<Self> {
30 Ok(if s == "?" {
31 EditQuery::NewAnonimous
32 } else {
33 EditQuery::Query(s.parse()?)
34 })
35 }
36}
37impl_ser_by_to_string!(EditQuery<ScriptOrDirQuery>);
38impl_ser_by_to_string!(EditQuery<ListQuery>);
39
40#[derive(Debug, Display, Clone)]
41pub enum DirQuery {
42 #[display(fmt = "/")]
43 Root,
44 #[display(fmt = "{}/", _0)]
45 NonRoot(ConcreteScriptName),
46}
47impl DirQuery {
48 pub fn join(self, other: &ScriptName) -> ConcreteScriptName {
69 match (self, other) {
70 (Self::Root, ScriptName::Anonymous(id)) => ConcreteScriptName::new_id(*id),
71 (Self::Root, ScriptName::Named(n)) => n.stem(),
72 (Self::NonRoot(mut dir), ScriptName::Anonymous(id)) => {
73 dir.join_id(*id);
74 dir
75 }
76 (Self::NonRoot(mut dir), ScriptName::Named(n)) => {
77 dir.join(n);
78 dir
79 }
80 }
81 }
82}
83
84#[derive(Debug, Display)]
85pub enum ScriptOrDirQuery {
86 #[display(fmt = "{}", _0)]
87 Script(ScriptName),
88 #[display(fmt = "{}", _0)]
89 Dir(DirQuery),
90}
91impl FromStr for ScriptOrDirQuery {
92 type Err = DisplayError;
93 fn from_str(s: &str) -> DisplayResult<Self> {
94 Ok(if s == "/" {
95 ScriptOrDirQuery::Dir(DirQuery::Root)
96 } else if s.ends_with('/') {
97 let s = &s[0..s.len() - 1];
98 ScriptOrDirQuery::Dir(DirQuery::NonRoot(ConcreteScriptName::new(s.into())?))
99 } else {
100 ScriptOrDirQuery::Script(s.parse()?)
101 })
102 }
103}
104impl_ser_by_to_string!(ScriptOrDirQuery);
105
106#[derive(Debug, Display)]
107pub enum ListQuery {
108 #[display(fmt = "{}", _1)]
109 Pattern(Regex, String, bool),
110 #[display(fmt = "{}", _0)]
111 Query(ScriptQuery),
112}
113impl FromStr for ListQuery {
114 type Err = DisplayError;
115 fn from_str(s: &str) -> DisplayResult<Self> {
116 if s.contains('*') {
117 let s = s.to_owned();
119 let re = s.replace(".", r"\.");
120 let re = re.replace("*", ".*");
121 let (re, bang) = if re.ends_with('!') {
122 (&re[0..re.len() - 1], true)
123 } else {
124 (&re[..], false)
125 };
126 match Regex::new(&format!("^{re}$",)) {
127 Ok(re) => Ok(ListQuery::Pattern(re, s, bang)),
128 Err(e) => {
129 log::error!("正規表達式錯誤:{}", e);
130 RegexCode.to_display_res(s)
131 }
132 }
133 } else {
134 Ok(ListQuery::Query(s.parse()?))
135 }
136 }
137}
138impl_ser_by_to_string!(ListQuery);
139
140#[derive(Debug, Clone, Eq, PartialEq)]
141pub struct ScriptQuery {
142 inner: ScriptQueryInner,
143 bang: bool,
144}
145impl Default for ScriptQuery {
146 fn default() -> Self {
147 ScriptQuery {
148 inner: ScriptQueryInner::Prev(none0_usize(1)),
149 bang: false,
150 }
151 }
152}
153impl std::fmt::Display for ScriptQuery {
154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155 match &self.inner {
156 ScriptQueryInner::Fuzz(fuzz) => write!(f, "{}", fuzz),
157 ScriptQueryInner::Exact(e) => write!(f, "={}", e),
158 ScriptQueryInner::Prev(p) => write!(f, "^{}", p),
159 }?;
160 if self.bang {
161 write!(f, "!")?;
162 }
163 Ok(())
164 }
165}
166impl_ser_by_to_string!(ScriptQuery);
167
168#[derive(Debug, Clone, Eq, PartialEq)]
169enum ScriptQueryInner {
170 Fuzz(String),
171 Exact(ScriptName),
172 Prev(NonZeroUsize),
173}
174impl IntoScriptName for ScriptQuery {
175 fn into_script_name(self) -> Result<ScriptName> {
176 match self.inner {
177 ScriptQueryInner::Fuzz(s) => s.into_script_name(),
178 ScriptQueryInner::Exact(name) => Ok(name),
179 _ => panic!("歷史查詢沒有名字"),
180 }
181 }
182}
183
184fn none0_usize(n: usize) -> NonZeroUsize {
185 NonZeroUsize::new(n).unwrap()
186}
187fn parse_prev(s: &str) -> Result<NonZeroUsize> {
188 let mut is_pure_prev = true;
190 for ch in s.chars() {
191 if ch != '^' {
192 is_pure_prev = false;
193 break;
194 }
195 }
196 if is_pure_prev {
197 return Ok(none0_usize(s.len()));
198 }
199 match s[1..s.len()].parse::<NonZeroUsize>() {
201 Ok(prev) => Ok(prev),
202 Err(e) => ScriptQueryCode
203 .to_res(s.to_owned())
204 .context(format!("解析整數錯誤:{}", e)),
205 }
206}
207impl FromStr for ScriptQuery {
208 type Err = DisplayError;
209 fn from_str(mut s: &str) -> DisplayResult<Self> {
210 let bang = if s.ends_with('!') {
211 if s == "!" {
212 return Ok(ScriptQuery {
213 inner: ScriptQueryInner::Prev(none0_usize(1)),
214 bang: true,
215 });
216 }
217 s = &s[..s.len() - 1];
218 true
219 } else {
220 false
221 };
222 let inner = if s.starts_with('=') {
223 s = &s[1..s.len()];
224 let name = s.to_owned().into_script_name()?;
225 ScriptQueryInner::Exact(name)
226 } else if s == "-" {
227 ScriptQueryInner::Prev(none0_usize(1))
228 } else if s.starts_with('^') {
229 ScriptQueryInner::Prev(parse_prev(s)?)
230 } else {
231 ScriptName::valid(s, true, true, true).context("模糊搜尋仍需符合腳本名格式!")?; ScriptQueryInner::Fuzz(s.to_owned())
233 };
234 Ok(ScriptQuery { inner, bang })
235 }
236}