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] Select,
10 Deselect,
11 Toggle,
12 CycleAll,
13 Accept,
14 Quit(Exit),
15
16 ChangeHeader(String),
18 CyclePreview,
19 Preview(String), SwitchPreview(Option<u8>), SetPreview(Option<u8>), 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 HistoryUp,
35 HistoryDown,
36 ChangePrompt,
37 ChangeQuery,
38 ToggleWrap,
39
40 ForwardChar,
42 BackwardChar,
43 ForwardWord,
44 BackwardWord,
45 DeleteChar,
46 DeleteWord,
47 DeleteLineStart,
48 DeleteLineEnd,
49 Cancel,
50
51 Up(Count),
53 Down(Count),
54 PreviewUp(Count),
55 PreviewDown(Count),
56 PreviewHalfPageUp,
57 PreviewHalfPageDown,
58 Pos(i32),
59
60 Redraw
62}
63
64
65
66
67impl 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 $( stringify!($unit) => Ok($enum::$unit), )*
145
146 $( 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 $( 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 $( 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 Execute,
210 Become,
211 Reload,
212 Print,
213 ChangeHeader,
214 Preview,
215
216 SetInput,
217 Column,
218 Pos;
219 Up,
221 Down,
222 PreviewUp,
223 PreviewDown,
224 Quit;
225
226 SwitchPreview,
228 SetPreview
229);
230
231use crate::{impl_int_wrapper};
232impl_int_wrapper!(Exit, i32, 1);
233impl_int_wrapper!(Count, u16, 1);