matchmaker/
action.rs

1use std::{
2    fmt::{self, Debug, Display},
3    mem::discriminant,
4    str::FromStr,
5};
6
7use cli_boilerplate_automation::impl_transparent_wrapper;
8use serde::{Deserialize, Serialize, Serializer};
9
10use crate::{
11    MAX_ACTIONS, SSS,
12    render::{Effects, MMState},
13    utils::serde::StringOrVec,
14};
15
16#[derive(Debug, Clone, Default)]
17pub enum Action<A: ActionExt = NullActionExt> {
18    #[default] // used to satisfy enumstring
19    /// Add item to selections
20    Select,
21    /// Remove item from selections
22    Deselect,
23    /// Toggle item in selections
24    Toggle,
25    CycleAll,
26    ClearAll,
27    Accept,
28    // Returns MatchError::Abort
29    Quit(Exit),
30
31    // UI
32    CyclePreview,
33    Preview(String),           // if match: hide, else match
34    Help(String),              // content is shown in preview, empty for default help display
35    SwitchPreview(Option<u8>), // n => ^ but with layout + layout_cmd, None => just toggle visibility
36    SetPreview(Option<u8>),    // n => set layout, None => set current layout cmd
37
38    ToggleWrap,
39    ToggleWrapPreview,
40
41    // Programmable
42    /// Pauses the tui display and the event loop, and invokes the handler for [`crate::message::Interrupt::Execute`]
43    /// The remaining actions in the buffer are still processed
44    Execute(String),
45    /// Exits the tui and invokes the handler for [`crate::message::Interrupt::Become`]
46    Become(String),
47    /// Restarts the matcher-worker and invokes the handler for [`crate::message::Interrupt::Reload`]
48    Reload(String),
49
50    /// Invokes the handler for [`crate::message::Interrupt::Print`]
51    /// See also: [`crate::Matchmaker::register_print_handler`]
52    Print(String),
53
54    SetInput(String),
55    SetHeader(Option<String>),
56    SetFooter(Option<String>),
57    SetPrompt(Option<String>),
58    Column(usize),
59    CycleColumn,
60
61    // Unimplemented
62    HistoryUp,
63    HistoryDown,
64    ChangePrompt,
65    ChangeQuery,
66
67    // Edit
68    ForwardChar,
69    BackwardChar,
70    ForwardWord,
71    BackwardWord,
72    DeleteChar,
73    DeleteWord,
74    DeleteLineStart,
75    DeleteLineEnd,
76    Cancel, // clear input
77    InputPos(i32),
78
79    // Navigation
80    Up(Count),
81    Down(Count),
82    PreviewUp(Count),
83    PreviewDown(Count),
84    PreviewHalfPageUp,
85    PreviewHalfPageDown,
86    Pos(i32),
87
88    // Other/Experimental/Debugging
89    Input(char),
90    Redraw,
91    Custom(A),
92    Overlay(usize),
93}
94
95impl<A: ActionExt> serde::Serialize for Action<A> {
96    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
97    where
98        S: serde::Serializer,
99    {
100        serializer.serialize_str(&self.to_string())
101    }
102}
103
104impl<A: ActionExt> PartialEq for Action<A> {
105    fn eq(&self, other: &Self) -> bool {
106        discriminant(self) == discriminant(other)
107    }
108}
109impl<A: ActionExt> Eq for Action<A> {}
110
111// --------- ACTION_EXT ------------------
112#[derive(Debug, Clone, Default, PartialEq)]
113pub struct NullActionExt {}
114
115impl ActionExt for NullActionExt {}
116
117impl fmt::Display for NullActionExt {
118    fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
119        Ok(())
120    }
121}
122
123impl std::str::FromStr for NullActionExt {
124    type Err = ();
125
126    fn from_str(_: &str) -> Result<Self, Self::Err> {
127        Err(())
128    }
129}
130
131pub trait ActionExt: Debug + Clone + FromStr + Display + PartialEq + SSS {}
132
133pub type ActionExtHandler<T, S, A> = fn(A, &MMState<'_, T, S>) -> Effects;
134pub type ActionAliaser<T, S, A> = fn(Action<A>, &MMState<'_, T, S>) -> Actions<A>;
135pub use arrayvec::ArrayVec;
136/// # Example
137/// ```rust
138///     use matchmaker::{action::{Action, Actions, acs}, render::MMState};
139///     pub fn fsaction_aliaser(
140///         a: Action,
141///         state: &MMState<'_, String, String>,
142///     ) -> Actions {
143///         match a {
144///             Action::Custom(_) => {
145///               log::debug!("Ignoring custom action");
146///               acs![]
147///             }
148///             _ => acs![a], // no change
149///         }
150///     }
151/// ```
152#[macro_export]
153macro_rules! acs {
154    ( $( $x:expr ),* $(,)? ) => {
155        {
156            $crate::action::Actions::from([$($x),*])
157        }
158    };
159}
160pub use crate::acs;
161
162/// # Example
163/// ```rust
164///     use matchmaker::{binds::{BindMap, bindmap, key}, action::Action};
165///     let default_config: BindMap = bindmap!(
166///        key!(alt-enter) => Action::Print("".into())
167///        // custom actions can be specified directly: key!(ctrl-c) => FsAction::Enter
168///    );
169/// ```
170#[macro_export]
171macro_rules! bindmap {
172    ( $( $k:expr => $v1:expr ),* $(,)? ) => {{
173        let mut map = $crate::binds::BindMap::new();
174        $(
175            map.insert($k.into(), $crate::action::Actions::from($v1));
176        )*
177        map
178    }};
179}
180// ----------- ACTIONS ---------------
181#[derive(Debug, Clone, PartialEq)]
182pub struct Actions<A: ActionExt = NullActionExt>(pub ArrayVec<Action<A>, MAX_ACTIONS>);
183impl<A: ActionExt> Default for Actions<A> {
184    fn default() -> Self {
185        Self(ArrayVec::new())
186    }
187}
188
189macro_rules! repeat_impl {
190    ($($len:expr),*) => {
191        $(
192            impl<A: ActionExt> From<[Action<A>; $len]> for Actions<A> {
193                fn from(arr: [Action<A>; $len]) -> Self {
194                    Actions(ArrayVec::from_iter(arr))
195                }
196            }
197
198            impl<A: ActionExt> From<[A; $len]> for Actions<A> {
199                fn from(arr: [A; $len]) -> Self {
200                    Actions(arr.into_iter().map(Action::Custom).collect())
201                }
202            }
203        )*
204    }
205}
206impl<A: ActionExt> From<[Action<A>; 0]> for Actions<A> {
207    fn from(empty: [Action<A>; 0]) -> Self {
208        Actions(ArrayVec::from_iter(empty))
209    }
210}
211repeat_impl!(1, 2, 3, 4, 5, 6);
212
213impl<A: ActionExt> From<Action<A>> for Actions<A> {
214    fn from(action: Action<A>) -> Self {
215        acs![action]
216    }
217}
218// no conflict because Action is local type
219impl<A: ActionExt> From<A> for Actions<A> {
220    fn from(action: A) -> Self {
221        acs![Action::Custom(action)]
222    }
223}
224
225// ---------- SERDE ----------------
226
227impl<'de, A: ActionExt> Deserialize<'de> for Actions<A> {
228    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
229    where
230        D: serde::Deserializer<'de>,
231    {
232        let helper = StringOrVec::deserialize(deserializer)?;
233        let strings = match helper {
234            StringOrVec::String(s) => vec![s],
235            StringOrVec::Vec(v) => v,
236        };
237
238        if strings.len() > MAX_ACTIONS {
239            return Err(serde::de::Error::custom(format!(
240                "Too many actions, max is {MAX_ACTIONS}."
241            )));
242        }
243
244        let mut actions = ArrayVec::new();
245        for s in strings {
246            let action = Action::from_str(&s).map_err(serde::de::Error::custom)?;
247            actions.push(action);
248        }
249
250        Ok(Actions(actions))
251    }
252}
253
254impl<A: ActionExt> Serialize for Actions<A> {
255    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
256    where
257        S: Serializer,
258    {
259        match self.0.len() {
260            1 => serializer.serialize_str(&self.0[0].to_string()),
261            _ => {
262                let strings: Vec<String> = self.0.iter().map(|a| a.to_string()).collect();
263                strings.serialize(serializer)
264            }
265        }
266    }
267}
268
269// ----- action serde
270
271macro_rules! impl_display_and_from_str_enum {
272    (
273        $($unit:ident),*;
274        $($tuple:ident),*;
275        $($tuple_default:ident),*;
276        $($tuple_option:ident),*;
277        $($tuple_string_default:ident),*
278    ) => {
279        impl<A: ActionExt> std::fmt::Display for Action<A> {
280            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281                match self {
282                    // Unit variants
283                    $( Self::$unit => write!(f, stringify!($unit)), )*
284
285                    // Tuple variants (always show inner)
286                    $( Self::$tuple(inner) => write!(f, concat!(stringify!($tuple), "({})"), inner), )*
287
288                    // Tuple variants with generic default fallback
289                    $( Self::$tuple_default(inner) => {
290                        if *inner == core::default::Default::default() {
291                            write!(f, stringify!($tuple_default))
292                        } else {
293                            write!(f, concat!(stringify!($tuple_default), "({})"), inner)
294                        }
295                    }, )*
296
297                    // Tuple variants with Option<T>
298                    $( Self::$tuple_option(opt) => {
299                        if let Some(inner) = opt {
300                            write!(f, concat!(stringify!($tuple_option), "({})"), inner)
301                        } else {
302                            write!(f, stringify!($tuple_option))
303                        }
304                    }, )*
305
306                    $( Self::$tuple_string_default(inner) => {
307                        if inner.is_empty() {
308                            write!(f, stringify!($tuple_string_default))
309                        } else {
310                            write!(f, concat!(stringify!($tuple_string_default), "({})"), inner)
311                        }
312                    }, )*
313
314                    Self::Custom(inner) => {
315                        write!(f, "{}", inner.to_string())
316                    }
317                    Self::Input(c) => {
318                        write!(f, "{c}")
319                    }
320                }
321            }
322        }
323
324        impl<A: ActionExt>  std::str::FromStr for Action<A> {
325            type Err = String;
326
327            fn from_str(s: &str) -> Result<Self, Self::Err> {
328                let (name, data) = if let Some(pos) = s.find('(') {
329                    if s.ends_with(')') {
330                        (&s[..pos], Some(&s[pos + 1..s.len() - 1]))
331                    } else {
332                        (s, None)
333                    }
334                } else {
335                    (s, None)
336                };
337
338                if let Ok(x) = name.parse::<A>() {
339                    return Ok(Self::Custom(x))
340                }
341                match name {
342                    $( stringify!($unit) => {
343                        if data.is_some() {
344                            Err(format!("Unexpected data for unit variant {}", name))
345                        } else
346                        {
347                            Ok(Self::$unit)
348                        }
349                    }, )*
350
351                    $( stringify!($tuple) => {
352                        let d = data
353                        .ok_or_else(|| format!("Missing data for {}", stringify!($tuple)))?
354                        .parse()
355                        .map_err(|_| format!("Invalid data for {}", stringify!($tuple)))?;
356                        Ok(Self::$tuple(d))
357                    }, )*
358
359                    $( stringify!($tuple_default) => {
360                        let d = match data {
361                            Some(val) => val.parse()
362                            .map_err(|_| format!("Invalid data for {}", stringify!($tuple_default)))?,
363                            None => Default::default(),
364                        };
365                        Ok(Self::$tuple_default(d))
366                    }, )*
367
368                    $( stringify!($tuple_option) => {
369                        let d = match data {
370                            Some(val) if !val.is_empty() => {
371                                Some(val.parse().map_err(|_| format!("Invalid data for {}", stringify!($tuple_option)))?)
372                            }
373                            _ => None,
374                        };
375                        Ok(Self::$tuple_option(d))
376                    }, )*
377
378                    $( stringify!($tuple_string_default) => {
379                        let d = match data {
380                            Some(val) if !val.is_empty() => val.to_string(),
381                            _ => String::new(),
382                        };
383                        Ok(Self::$tuple_string_default(d))
384                    }, )*
385
386                    _ => Err(format!("Unknown variant {}", s))
387                }
388            }
389        }
390    };
391}
392
393// call it like:
394impl_display_and_from_str_enum!(
395    Select, Deselect, Toggle, CycleAll, ClearAll, Accept, CyclePreview, CycleColumn,
396    PreviewHalfPageUp, PreviewHalfPageDown, HistoryUp, HistoryDown,
397    ChangePrompt, ChangeQuery, ToggleWrap, ToggleWrapPreview, ForwardChar,
398    BackwardChar, ForwardWord, BackwardWord, DeleteChar, DeleteWord,
399    DeleteLineStart, DeleteLineEnd, Cancel, Redraw;
400    // tuple variants
401    Execute, Become, Reload, Preview, SetInput, Column, Pos, InputPos;
402    // tuple with default
403    Up, Down, PreviewUp, PreviewDown, Quit, Overlay;
404    // tuple with option
405    SwitchPreview, SetPreview, SetPrompt, SetHeader, SetFooter;
406    // tuple_string_default
407    Print, Help
408);
409
410impl_transparent_wrapper!(Exit, i32, 1);
411impl_transparent_wrapper!(Count, u16, 1; derive(Copy));
412
413// --------------------------------------
414impl<A: ActionExt> IntoIterator for Actions<A> {
415    type Item = Action<A>;
416    type IntoIter = <ArrayVec<Action<A>, MAX_ACTIONS> as IntoIterator>::IntoIter;
417
418    fn into_iter(self) -> Self::IntoIter {
419        self.0.into_iter()
420    }
421}
422
423impl<'a, A: ActionExt> IntoIterator for &'a Actions<A> {
424    type Item = &'a Action<A>;
425    type IntoIter = <&'a ArrayVec<Action<A>, MAX_ACTIONS> as IntoIterator>::IntoIter;
426
427    fn into_iter(self) -> Self::IntoIter {
428        self.0.iter()
429    }
430}