1use std::{fmt::{self, Debug, Display}, mem::discriminant, str::FromStr};
2
3use cli_boilerplate_automation::impl_transparent_wrapper;
4use serde::{Deserialize, Serialize, Serializer};
5
6use crate::{MAX_ACTIONS, SSS, utils::serde::StringOrVec, render::{Effects, MMState}};
7
8#[derive(Debug, Clone, Default)]
9pub enum Action<A: ActionExt = NullActionExt> {
10 #[default] Select,
13 Deselect,
15 Toggle,
17 CycleAll,
18 ClearAll,
19 Accept,
20 Quit(Exit),
22
23 CyclePreview,
25 Preview(String), Help(String), SwitchPreview(Option<u8>), SetPreview(Option<u8>), ToggleWrap,
31 ToggleWrapPreview,
32
33 Execute(String),
37 Become(String),
39 Reload(String),
41
42 Print(String),
45
46 SetInput(String),
47 SetHeader(Option<String>),
48 SetFooter(Option<String>),
49 SetPrompt(Option<String>),
50 Column(usize),
51 CycleColumn,
52
53 HistoryUp,
55 HistoryDown,
56 ChangePrompt,
57 ChangeQuery,
58
59 ForwardChar,
61 BackwardChar,
62 ForwardWord,
63 BackwardWord,
64 DeleteChar,
65 DeleteWord,
66 DeleteLineStart,
67 DeleteLineEnd,
68 Cancel, InputPos(i32),
70
71 Up(Count),
73 Down(Count),
74 PreviewUp(Count),
75 PreviewDown(Count),
76 PreviewHalfPageUp,
77 PreviewHalfPageDown,
78 Pos(i32),
79
80 Redraw,
82 Custom(A),
83 Overlay(usize)
84}
85
86impl<A: ActionExt> serde::Serialize for Action<A> {
87 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
88 where
89 S: serde::Serializer,
90 {
91 serializer.serialize_str(&self.to_string())
92 }
93}
94
95impl<A: ActionExt> PartialEq for Action<A> {
96 fn eq(&self, other: &Self) -> bool {
97 discriminant(self) == discriminant(other)
98 }
99}
100impl<A: ActionExt> Eq for Action<A> {}
101
102#[derive(Debug, Clone, Default, PartialEq)]
104pub struct NullActionExt {}
105
106impl ActionExt for NullActionExt {}
107
108impl fmt::Display for NullActionExt {
109 fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
110 Ok(())
111 }
112}
113
114impl std::str::FromStr for NullActionExt {
115 type Err = ();
116
117 fn from_str(_: &str) -> Result<Self, Self::Err> {
118 Err(())
119 }
120}
121
122pub trait ActionExt: Debug + Clone + FromStr + Display + PartialEq + SSS
123{}
124
125pub type ActionExtHandler<T, S, A> = fn(A, &mut MMState<'_, T, S>) -> Effects;
126pub type ActionAliaser<T, S, A> = fn(Action<A>, &MMState<'_, T, S>) -> Actions<A>;
127pub use arrayvec::ArrayVec;
128#[macro_export]
145macro_rules! acs {
146 ( $( $x:expr ),* $(,)? ) => {
147 {
148 $crate::action::Actions::from([$($x),*])
149 }
150 };
151}
152pub use crate::acs;
153
154#[macro_export]
163macro_rules! bindmap {
164 ( $( $k:expr => $v1:expr ),* $(,)? ) => {{
165 let mut map = $crate::binds::BindMap::new();
166 $(
167 map.insert($k.into(), $crate::action::Actions::from($v1));
168 )*
169 map
170 }};
171}
172#[derive(Debug, Clone, PartialEq)]
174pub struct Actions<A: ActionExt = NullActionExt>(pub ArrayVec<Action<A>, MAX_ACTIONS>);
175impl<A: ActionExt> Default for Actions<A> {
176 fn default() -> Self {
177 Self(ArrayVec::new())
178 }
179}
180
181
182macro_rules! repeat_impl {
183 ($($len:expr),*) => {
184 $(
185 impl<A: ActionExt> From<[Action<A>; $len]> for Actions<A> {
186 fn from(arr: [Action<A>; $len]) -> Self {
187 Actions(ArrayVec::from_iter(arr))
188 }
189 }
190
191 impl<A: ActionExt> From<[A; $len]> for Actions<A> {
192 fn from(arr: [A; $len]) -> Self {
193 Actions(arr.into_iter().map(Action::Custom).collect())
194 }
195 }
196 )*
197 }
198}
199impl<A: ActionExt> From<[Action<A>; 0]> for Actions<A> {
200 fn from(empty: [Action<A>; 0]) -> Self {
201 Actions(ArrayVec::from_iter(empty))
202 }
203}
204repeat_impl!(1, 2, 3, 4, 5, 6);
205
206impl<A: ActionExt> From<Action<A>> for Actions<A> {
207 fn from(action: Action<A>) -> Self {
208 acs![action]
209 }
210}
211impl<A: ActionExt> From<A> for Actions<A> {
213 fn from(action: A) -> Self {
214 acs![Action::Custom(action)]
215 }
216}
217
218impl<'de, A: ActionExt> Deserialize<'de> for Actions<A> {
221 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
222 where
223 D: serde::Deserializer<'de>,
224 {
225 let helper = StringOrVec::deserialize(deserializer)?;
226 let strings = match helper {
227 StringOrVec::String(s) => vec![s],
228 StringOrVec::Vec(v) => v,
229 };
230
231 if strings.len() > MAX_ACTIONS {
232 return Err(
233 serde::de::Error::custom(format!("Too many actions, max is {MAX_ACTIONS}.")
234 )
235 );
236 }
237
238 let mut actions = ArrayVec::new();
239 for s in strings {
240 let action = Action::from_str(&s).map_err(serde::de::Error::custom)?;
241 actions.push(action);
242 }
243
244 Ok(Actions(actions))
245 }
246}
247
248impl<A: ActionExt> Serialize for Actions<A> {
249 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
250 where
251 S: Serializer,
252 {
253 match self.0.len() {
254 1 => serializer.serialize_str(&self.0[0].to_string()),
255 _ => {
256 let strings: Vec<String> = self.0.iter().map(|a| a.to_string()).collect();
257 strings.serialize(serializer)
258 }
259 }
260 }
261}
262
263macro_rules! impl_display_and_from_str_enum {
266 (
267 $($unit:ident),*;
268 $($tuple:ident),*;
269 $($tuple_default:ident),*;
270 $($tuple_option:ident),*;
271 $($tuple_string_default:ident),*
272 ) => {
273 impl<A: ActionExt> std::fmt::Display for Action<A> {
274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275 match self {
276 $( Self::$unit => write!(f, stringify!($unit)), )*
278
279 $( Self::$tuple(inner) => write!(f, concat!(stringify!($tuple), "({})"), inner), )*
281
282 $( Self::$tuple_default(inner) => {
284 if *inner == core::default::Default::default() {
285 write!(f, stringify!($tuple_default))
286 } else {
287 write!(f, concat!(stringify!($tuple_default), "({})"), inner)
288 }
289 }, )*
290
291 $( Self::$tuple_option(opt) => {
293 if let Some(inner) = opt {
294 write!(f, concat!(stringify!($tuple_option), "({})"), inner)
295 } else {
296 write!(f, stringify!($tuple_option))
297 }
298 }, )*
299
300 $( Self::$tuple_string_default(inner) => {
301 if inner.is_empty() {
302 write!(f, stringify!($tuple_string_default))
303 } else {
304 write!(f, concat!(stringify!($tuple_string_default), "({})"), inner)
305 }
306 }, )*
307
308 Self::Custom(inner) => {
309 write!(f, "{}", inner.to_string())
310 }
311 }
312 }
313 }
314
315 impl<A: ActionExt> std::str::FromStr for Action<A> {
316 type Err = String;
317
318 fn from_str(s: &str) -> Result<Self, Self::Err> {
319 let (name, data) = if let Some(pos) = s.find('(') {
320 if s.ends_with(')') {
321 (&s[..pos], Some(&s[pos + 1..s.len() - 1]))
322 } else {
323 (s, None)
324 }
325 } else {
326 (s, None)
327 };
328
329 if let Ok(x) = name.parse::<A>() {
330 return Ok(Self::Custom(x))
331 }
332 match name {
333 $( stringify!($unit) => {
334 if data.is_some() {
335 Err(format!("Unexpected data for unit variant {}", name))
336 } else
337 {
338 Ok(Self::$unit)
339 }
340 }, )*
341
342 $( stringify!($tuple) => {
343 let d = data
344 .ok_or_else(|| format!("Missing data for {}", stringify!($tuple)))?
345 .parse()
346 .map_err(|_| format!("Invalid data for {}", stringify!($tuple)))?;
347 Ok(Self::$tuple(d))
348 }, )*
349
350 $( stringify!($tuple_default) => {
351 let d = match data {
352 Some(val) => val.parse()
353 .map_err(|_| format!("Invalid data for {}", stringify!($tuple_default)))?,
354 None => Default::default(),
355 };
356 Ok(Self::$tuple_default(d))
357 }, )*
358
359 $( stringify!($tuple_option) => {
360 let d = match data {
361 Some(val) if !val.is_empty() => {
362 Some(val.parse().map_err(|_| format!("Invalid data for {}", stringify!($tuple_option)))?)
363 }
364 _ => None,
365 };
366 Ok(Self::$tuple_option(d))
367 }, )*
368
369 $( stringify!($tuple_string_default) => {
370 let d = match data {
371 Some(val) if !val.is_empty() => val.to_string(),
372 _ => String::new(),
373 };
374 Ok(Self::$tuple_string_default(d))
375 }, )*
376
377 _ => Err(format!("Unknown variant {}", s))
378 }
379 }
380 }
381 };
382}
383
384impl_display_and_from_str_enum!(
386 Select, Deselect, Toggle, CycleAll, ClearAll, Accept, CyclePreview, CycleColumn,
387 PreviewHalfPageUp, PreviewHalfPageDown, HistoryUp, HistoryDown,
388 ChangePrompt, ChangeQuery, ToggleWrap, ToggleWrapPreview, ForwardChar,
389 BackwardChar, ForwardWord, BackwardWord, DeleteChar, DeleteWord,
390 DeleteLineStart, DeleteLineEnd, Cancel, Redraw;
391 Execute, Become, Reload, Preview, SetInput, Column, Pos, InputPos;
393 Up, Down, PreviewUp, PreviewDown, Quit, Overlay;
395 SwitchPreview, SetPreview, SetPrompt, SetHeader, SetFooter;
397 Print, Help
399);
400
401impl_transparent_wrapper!(Exit, i32, 1);
402impl_transparent_wrapper!(Count, u16, 1; derive(Copy));
403