matchmaker/
action.rs

1use std::{mem::discriminant, str::FromStr};
2
3use serde::Deserialize;
4use strum_macros::Display;
5
6#[derive(Debug, Display, Clone, Deserialize, Default)]
7pub enum Action {
8    #[default] // used to satisfy enumstring
9    Select,
10    Deselect,
11    Toggle,
12    CycleAll,
13    Accept,
14    Quit(Exit),
15    
16    // UI
17    ChangeHeader(String),
18    CyclePreview,
19    Preview(String), // if match: hide, else match
20    SwitchPreview(Option<u8>), // n => ^ but with layout + layout_cmd, 0 => just toggle visibility
21    SetPreview(Option<u8>), // n => set layout, 0 => set current layout cmd
22    
23    // Programmable
24    Execute(String),
25    Become(String),
26    Reload(String),
27    Print(String),
28    
29    SetInput(String),
30    SetHeader(String),
31    SetPrompt(String),
32    Column(usize),
33    // Unimplemented
34    HistoryUp,
35    HistoryDown,
36    ChangePrompt,
37    ChangeQuery,
38    ToggleWrap,
39    
40    // Edit
41    ForwardChar,
42    BackwardChar,
43    ForwardWord,
44    BackwardWord,
45    DeleteChar,
46    DeleteWord,
47    DeleteLineStart,
48    DeleteLineEnd,
49    Cancel,
50    
51    // Navigation
52    Up(Count),
53    Down(Count),
54    PreviewUp(Count),
55    PreviewDown(Count),
56    PreviewHalfPageUp,
57    PreviewHalfPageDown,
58    Pos(i32),
59    
60    // Experimental/Debugging
61    Redraw
62}
63
64
65
66
67// -----------------------------------------------------------------------------------------------------------------------
68
69
70impl PartialEq for Action {
71    fn eq(&self, other: &Self) -> bool {
72        discriminant(self) == discriminant(other)
73    }
74}
75
76impl Eq for Action {}
77
78impl std::hash::Hash for Action {
79    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
80        discriminant(self).hash(state);
81    }
82}
83
84#[derive(Debug, Clone)]
85pub struct Actions(pub Vec<Action>);
86
87impl<const N: usize> From<[Action; N]> for Actions {
88    fn from(arr: [Action; N]) -> Self {
89        Actions(arr.into())
90    }
91}
92
93impl<'de> Deserialize<'de> for Actions {
94    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
95    where
96    D: serde::Deserializer<'de>,
97    {
98        #[derive(Debug, Deserialize)]
99        #[serde(untagged)]
100        enum Helper {
101            Single(String),
102            Multiple(Vec<String>),
103        }
104        
105        let helper = Helper::deserialize(deserializer)?;
106        let strings = match helper {
107            Helper::Single(s) => vec![s],
108            Helper::Multiple(v) => v,
109        };
110        
111        let mut actions = Vec::with_capacity(strings.len());
112        for s in strings {
113            let action = Action::from_str(&s).map_err(serde::de::Error::custom)?;
114            actions.push(action);
115        }
116        
117        Ok(Actions(actions))
118    }
119}
120
121macro_rules! impl_from_str_enum {
122    ($enum:ident,
123        $($unit:ident),*;
124        $($tuple:ident),*;
125        $($tuple_default:ident),*;
126        $($tuple_option:ident),*
127    ) => {
128        impl std::str::FromStr for $enum {
129            type Err = String;
130            
131            fn from_str(s: &str) -> Result<Self, Self::Err> {
132                let (name, data) = if let Some(pos) = s.find('(') {
133                    if s.ends_with(')') {
134                        (&s[..pos], Some(&s[pos + 1..s.len() - 1]))
135                    } else {
136                        (s, None)
137                    }
138                } else {
139                    (s, None)
140                };
141                
142                match name {
143                    // Unit variants
144                    $( stringify!($unit) => Ok($enum::$unit), )*
145                    
146                    // Tuple variants (data required)
147                    $( stringify!($tuple) => {
148                        let d = data
149                        .ok_or_else(|| format!("Missing data for {}", stringify!($tuple)))?
150                        .parse()
151                        .map_err(|_| format!("Invalid data for {}", stringify!($tuple)))?;
152                        Ok($enum::$tuple(d))
153                    }, )*
154                    
155                    // Tuple variants with default fallback
156                    $( stringify!($tuple_default) => {
157                        let d = match data {
158                            Some(val) => val.parse()
159                            .map_err(|_| format!("Invalid data for {}", stringify!($tuple_default)))?,
160                            None => Default::default(),
161                        };
162                        Ok($enum::$tuple_default(d))
163                    }, )*
164                    
165                    // Tuple variants that produce Option<T>
166                    $( stringify!($tuple_option) => {
167                        let d = match data {
168                            Some(val) if !val.is_empty() => {
169                                Some(val.parse().map_err(|_| format!("Invalid data for {}", stringify!($tuple_option)))?)
170                            },
171                            _ => None,
172                        };
173                        Ok($enum::$tuple_option(d))
174                    }, )*
175                    
176                    _ => Err(format!("Unknown variant {}", name)),
177                }
178            }
179        }
180    };
181}
182
183impl_from_str_enum!(
184    Action,
185    Select,
186    Deselect,
187    Toggle,
188    CycleAll,
189    Accept,
190    CyclePreview,
191    
192    PreviewHalfPageUp,
193    PreviewHalfPageDown,
194    HistoryUp,
195    HistoryDown,
196    ChangePrompt,
197    ChangeQuery,
198    ToggleWrap,
199    ForwardChar,
200    BackwardChar,
201    ForwardWord,
202    BackwardWord,
203    DeleteChar,
204    DeleteWord,
205    DeleteLineStart,
206    DeleteLineEnd,
207    Cancel;
208    // tuple variants
209    Execute,
210    Become,
211    Reload,
212    Print,
213    ChangeHeader,
214    Preview,
215    
216    SetInput,
217    Column,
218    Pos;
219    // tuples with defaults
220    Up,
221    Down,
222    PreviewUp,
223    PreviewDown,
224    Quit;
225    
226    // tuple options
227    SwitchPreview,
228    SetPreview
229);
230
231use crate::{impl_int_wrapper};
232impl_int_wrapper!(Exit, i32, 1);
233impl_int_wrapper!(Count, u16, 1);